Assume this is pretty easy but having a hard time printing out NodeSeq of html tags with newlines (so when I view source in web browser, I can scan top-to-bottom)
As is, the NodeSeq is printed as one long line.
example code:
listOfPaths map ( jsNode(_) ) reduce (_++_)
def jsNode(path: String): NodeSeq =
<script type="text/javascript" src={"/static/js/"+path}></script>
So, how to get a \n at end of each node?
Thanks
This is really a job for whatever you're using to render the HTML. For example, if you're using scala.xml.PrettyPrinter, you could do something like this:
val printer = new xml.PrettyPrinter(80, 2)
val paths = List("script-1.js", "script-2.js")
val header = <head>{paths map ( jsNode(_) ) reduce (_++_)}</head>
Now when you call printer.format(header), you'll get the following:
<head>
<script type="text/javascript" src="/static/js/script-1.js"></script>
<script type="text/javascript" src="/static/js/script-2.js"></script>
</head>
Note that the first argument to the PrettyPrinter constructor specifies the page width, and the second the number of spaces to indent.
If you just want something quick and dirty, you could drop a text node between (or after) the elements:
paths map ( jsNode(_) ) reduce (_++ Text("\n") ++ _)
But the other solution is almost always preferable.
Related
I have a template that looks like this:
<lift:surround name="default" at="page-content">
<lift:bind-at name="page-title">Home</lift:bind-at>
...
</lift:surround>
The default template looks like this:
<html>
<head>
<title>Title Prefix | </title>
</head>
<body>
<h1><lift:bind name="page-title" /></h1>
<div id="page-content">
<lift:bind name="page-content" />
</div>
</body>
</html>
I want to use a snippet to replace the <title> content with a string that combines "Title Prefix" and the value of <lift:bind-at name="page-title"> (ie: "Home"). I want to contine to use that same value inside the <h1> in the <body>
How can I access a bind-at value from within a snippet that's used in the surrounding template?
I don't believe you can do what you are looking to do with bind-at directives, or at least, I haven't found a way. You should be able to use a snippet to accomplish something similar though.
For example, if you are using SiteMap, the following should be roughly equivalent.
class TitleSnippet {
//Store the value in a requestVar
private var titleVar:String = ""
def first = {
//Retrieve the title for the current Loc as defined in the Sitemap
val locTitle = for (request <- S.request;
loc <- request.location) yield loc.title
//Retrieve the text portion of the tag, and append it to the sitemap title.
//If no sitemap title exists, just display the text
"* *" #> { ns =>
val myTitle = locTitle.map{ t =>
"%s | %s".format(ns.text, t)
} openOr ns.text
titleVar = myTitle
Text(myTitle)
}
}
def title = {
"* *" #> titleVar
}
}
Then, in your template, all you'd have to do is say:
<title data-lift="TitleSnippet.first">Home</title>
So, if we had a page defined like this in the sitemap:
Menu("Sub Page 1") / "subpage"
If everything worked, you should see a title like: <title>Home | Sub Page 1</title> and if you need it elsewhere on the page, all you would have to do is: <h1 data-lift="TitleSnippet.title"></h1>.
If you need access from other snippets, you can also break out titleVar into a companion object and use a RequestVar.
Is there a way I can modify dynamically the param of a snippet?
E.g. If I call this URL
host:port/a_page?name=myname
I would like that my page look like this:
<div class="lift:surround?with=default;at=content">
<div class="lift:comet?type=MySnippet;name=myname" >
...
</div>
</div>
Is that even possible? I tried using some javascript in order to extract the param from the url and putting it in the class attribute of the div but in my understanding that won't work becase the scripts will always execute after lift framework does it's magic.
Thanks in advance! Any help is really appreciated.
I used both tips provided to make it work, like ajantis mentioned reading the param directly from snippet is the easiest way but doesnt work in a comet call. Rogach solution works.
So the solution is:
<div class="lift:Ex.wrap">
<div id="myid"></div>
</div>
def wrap = {
val name = "lift:comet?type=MySnippet;name=" + S.param("name").openOr("...")
"#myid" #> <div id="myid" class={name} ></div>
}
Why not just extract http parameter inside snippet processing? i.e.
def render = {
val name = S.param("name").openOr("...")
....
}
You can try wrapping that comet snippet in other snippet, which would transform xml and add that name=myname to class. Like:
<div class="lift:Ex.wrap">
<div class="lift:comet?type=MySnippet"></div>
</div>
class Ex {
def wrap = { (n: NodeSeq) =>
// example transformation
// AntiXML syntax
import com.codecommit.antixml._;
val comet = n \ "div" head;
val comet2 =
comet.copy(attrs = comet.attrs +
("class" -> (comet.attrs("class") + ";name=myname")))
n \ "div" updated (0, comet2) unselect
}
}
I have the below (simplified) code, which uses the following source:
<html>
<p>line 1</p>
<div>
<a>line 2</a>
</div>
</html>
soup = BeautifulSoup('<html><p>line 1</p><div><a>line 2</a></div></html>')
ele = soup.find('p').nextSibling
somehow_print_tag_of_ele_here
I want to get the tag of ele, in this case "div". However, I only seem to be able to get the tag of its children. Am I missing something simple? I thought that I could do ele.tag.name, but that is an exception since tag is None.
#Below correctly prints the div element "<div><a>line 2</a></div>"
print ele
#Below prints "None". Printing tag.name is an exception since tag is None
print ele.tag
#Below prints "a", the child of ele
allTags = ele.findAll(True)
for e in allTags:
print e.name
At this point, I am considering doing something along the way of getting the parent of ele, then getting the tags of parent's children and, having counted how many upper siblings ele has, counting down to the correct child tag. That seems ridiculous.
ele is already a tag, try doing this:
soup = BeautifulSoup('<html><p>line 1</p><div><a>line 2</a></div></html>')
print(soup.find('p').nextSibling.name)
so in your example it would be just
print(ele.name)
You can access anything inside an element as if accessing a dictionary.
Let's say you have an element like this one.
<input id="__VIEWSTATE3" name="__VIEWSTATE3" type="hidden" value="MwqzeTH4"/>
You can access each property like this
print(elem["id"])
# prints __VIEWSTATE3
print(soup.find('h1',id_='pdp_product_title'))
it doesnot print any detail please solved this
<h1 id="pdp_product_title" class="headline-2 css-zis9ta" data-test="product-title">Nike Air Force 1 Shadow</h1>
I've searched everywhere and what I most found was doc.xpath('//element[#class="classname"]'), but this does not work no matter what I try.
code I'm using
import lxml.html
def check():
data = urlopen('url').read();
return str(data);
doc = lxml.html.document_fromstring(check())
el = doc.xpath("//div[#class='test']")
print(el)
It simply prints an empty list.
Edit:
How odd. I used google as a test page and it works fine there, but it doesn't work on the page I was using (youtube)
Here's the exact code I'm using.
import lxml.html
from urllib.request import urlopen
import sys
def check():
data = urlopen('http://www.youtube.com/user/TopGear').read(); #TopGear as a test
return data.decode('utf-8', 'ignore');
doc = lxml.html.document_fromstring(check())
el = doc.xpath("//div[#class='channel']")
print(el)
The TopGear page that you use for testing doesn't have any <div class="channel"> elements. But this works (for example):
el = doc.xpath("//div[#class='channel-title-container']")
Or this:
el = doc.xpath("//div[#class='a yb xr']")
To find <div> elements with a class attribute that contains the string channel, you could use
el = doc.xpath("//div[contains(#class, 'channel')]")
You can use lxml.cssselect to simplify class and id request: http://lxml.de/dev/cssselect.html
HTML uses classes (a lot), which makes them convenient to hook XPath queries. However XPath has no knowledge/support of CSS classes (or even space-separated lists) which makes classes a pain in the ass to check: the canonically correct way to look for elements having a specific class is:
//*[contains(concat(' ', normalize-space(#class), ' '), '$className')]
In your case this is
el = doc.xpath(
"//div[contains(concat(' ', normalize-space(#class), ' '), 'channel')]"
)
# print(el)
# [<Element div at 0x7fa44e31ccc8>, <Element div at 0x7fa44e31c278>, <Element div at 0x7fa44e31cdb8>]
or use own XPath function hasclass(*classes)
def _hasaclass(context, *cls):
return "your implementation ..."
xpath_utils = etree.FunctionNamespace(None)
xpath_utils['hasaclass'] = _hasaclass
el = doc.xpath("//div[hasaclass('channel')]")
im just starting with lift and scala and have a problem i dont realy understand.
i have the folowing index.html
<html>
<head><title>title</title></head>
<body>
<table>
<lift:Members.list>
<tr>
<td><m:nick/></td>
</tr>
</lift:Members.list>
</table>
</body>
</html>
And the following snippet:
class Members {
def list(xhtml: NodeSeq) =
Member.findAll.flatMap(member => bind("m",xhtml
,"nick" -> member.nickName
))
}
for some reason i get the following error. ive tried alot of things but cant get it to work. whats wrong?
XML Parsing Error: prefix not bound to a namespace
Location: http://localhost:8080/hazardlift-1.0-SNAPSHOT/
Line Number 8, Column 25:<td><m:nick></m:nick></td>
-----------------------------^
Maybe lift doesn't get how to handle your return value. Try forcing an implicit conversion to NodeSeq by specifing it as returntype.
....
def list(xhtml: NodeSeq) : NodeSeq =
....
I just found another cause of this error - an unresolved tag.
I had this HTML:
<div >
<h3>Request Information</h3>
<lift:DetailedRequestData.renderContent>
<f:rowTag></f:rowTag>
</lift:DetailedRequestData.renderContent>
</div>
I had written this for renderContent:
def renderContent(ns: NodeSeq): NodeSeq = {
val key = beginTrans(DisplayData.logger)
var result = ns
try {
var requestID = DisplayData.getParameter("request")
bind("f", ns, "rowTag" -> <p>Request ID: {requestID}</p>)
}
catch {
case t: Throwable => DisplayData.logger.error("[DetailedRequestData$.renderContent] ", t)
}
endTrans(DisplayData.logger, key)
result
}
Since I had not assigned the result of the bind to result, I was returning the unmodified NodeSeq and got the same prefix not bound to a namespace error. Changing the one statement to:
result = bind("f", ns, "rowTag" -> <p>Request ID: {requestID}</p>)
Yes, this was my own stupid fault, but by documenting the problem here, hopefully I will save someone else from having this same problem and not knowing why.