I guess I could see something that keeps track of which item to show and
advances it each time, but I think it's still a good idea to pass the item
explicitly down to subsidiary "snippets". You can still break up each of the
sub snippets, the only difference is you call them without the lift: prefix.
This is analogous to composing functions.
Maybe something like this?
object MySnippet {
object nextItems extends RequestVar[Iterator[ModelObject]](calcNextItems)
def nextItem: Box[ModelObject] = {
val it = nextItems.is
if (it.hasNext) Full(it.next) else Empty
}
private def calcNextItems: List[ModelObject] =
for (startIndex <- S.attr("index").flatMap(asInt).openOr 0)
yield ModelObject.findAll(StartAt(startIndex), MaxRows(2)).elements
}
class MySnippet extends DispatchSnippet {
import MySnippet._
val dispatch: DispatchIt = {
case "displayNext" => displayNextItem
}
def displayNext(ns: NodeSeq): NodeSeq) =
nextItem.map(renderItem(ns) _).openOr(NodeSeq.Empty)
def renderItem(item: ModelObject)(ns: NodeSeq): NodeSeq =
bind("item", ns, "which" -> item.title,
"points" -> renderPointsOf(item) _,
// other "sub" snippet which need a current item here
"picture" -> renderPictureOf(item) _)
def renderPointsOf(item: ModelObject)(ns: NodeSeq): NodeSeq =
item.points.flatMap(point => bind("point", ns, "name" -> point.name))
def renderPictureOf(item: ModelObject)(ns: NodeSeq): NodeSeq =
<img src={ ... } />
}
And template item-index-2up.html:
<h1>Items</h1>
<lift:Item.displayNext eager_eval="true">
<lift:embed what="standard-item-template" />
</lift:Item.displayNext>
<lift:Item.displayNext eager_eval="true">
<lift:embed what="standard-item-template" />
</lift:Item.displayNext>
templates-hidden/standard-item-template.html:
<h2><item:which /></h2>
<ul>
<item:points><li><point:name /></li></item:points>
</ul>
<item:picture />
I fear I'm not quite understanding what you're looking for, though.
-Ross
On Dec 23, 2009, at 9:43 PM, Alex Black wrote:
> Hi Ross, I am learning more about Lift every day :)
>
> Reflecting that last point back to you, if I make it just one snippet
> call, then that snippet call can pass parameters to inner snippet
> functions, e.g. first or second.
>
> So, that works, but I as you mentioned, I think that snippet will get
> complex. You then pointed out it can be broken into sub snippet
> calls, which helps, but I still feel like we'd have this big complex
> snippet call that is tightly coupled to the big complex xhtml block.
> E.g. it'd be like doing an entire page in one snippet call instead of
> lots of little ones.
>
> Btw, the 'blocks' I keep referring to are product (camera)
> specifications, so its like we're displaying two products on one page.
>
> I had an idea, could the first snippet set a requestVar for the
> current item, then the other snippet calls read that? e.g.
>
> <h1>Items</h1>
>
> <lift:Item.firstAndSecond>
> <h2><item:which></h2>
> <ul>
> <lift:Item.points>
> <li><point:name />
> </lift:Item.points>
> </ul>
> <lift:item.picture />
> </lift:Item.firstAndSecond>
>
> // Emit the block twice, once for each item (first,second).
> def firstAndSecond(xhtml: NodeSeq): NodeSeq = {
> List(first,second).flatMap( item => renderItem(xhtml, item) )
> }
>
> def renderItem( xhtml: NodeSeq, item: Item): NodeSeq = {
> _currentItem(item)
> // do stuff
> }
>
> def points( xhtml: NodeSeq ): NodeSeq = {
> // do stuff based on _currentItem.is
> }
>
> - Alex
>
> On Dec 23, 5:05 pm, Ross Mellgren <[email protected]> wrote:
>> So, with nested snippets you have two basic execution model options:
>>
>> 1) the default, the inner snippets get executed after the outer
>> snippet has executed completely. the outer snippet sees as it's
>> content the original content from the template, complete with
>> lift:Item.points tags and so on.
>> 2) when eager_eval="true" is specified on the outer snippet, the
>> inner snippets get executed completely before the outer snippet gets
>> executed. the outer snippet sees the _result_ of processing the
>> children from the template, so it sees whatever the lift:Item.points
>> snippet gives back.
>>
>> So, this means that fundamentally you couldn't tell the inner snippet
>> which is the current one, without rewriting the call to the inner
>> snippet (e.g. find the <lift:Item.points> tag and write in an
>> attribute -- this seems like a grody hack)
>>
>> If you really want an inner snippet running _during_ the processing of
>> the outer snippet, I think you need to do that
>> processSurroundAndInclude thing in the outer snippet code.
>>
>> If you're not tied to the inner snippet, then omit it, like this:
>>
>> class Item extends DispatchSnippet {
>> val dispatch: DispatchIt = {
>> case "firstAndSecond" => firstAndSecond
>> }
>>
>> def firstAndSecond(ns: NodeSeq): NodeSeq =
>> List(("first", firstSeqOfThings), ("second",
>> secondSeqOfThings)).flatMap {
>> case (label, items) =>
>> bind("item", ns,
>> "which" -> label,
>> "points" -> { (ns: NodeSeq) => items.flatMap
>> { item =>
>> bind("point", ns, "name" -> item.name)
>> })
>> }
>>
>> }
>>
>> And the template would be:
>>
>> <h1>Items</h1>
>>
>> <lift:Item.firstAndSecond>
>> <h2><item:which /></h2>
>> <ul>
>> <item:points>
>> <li><point:name /></li>
>> </item:points>
>> </ul>
>> <item:picture />
>> </lift:Item.firstAndSecond>
>>
>> If you're concerned about embedding so much code in the snippet (which
>> seems to be why you want to break into multiple snippets?) then just
>> break them out into separate functions, like I showed you in the
>> foreach example.
>>
>> I think it helps to remember that snippets are really just XML -> XML
>> functions, and there's little special about them other than that they
>> can be named in templates. You can always call snippets from other
>> snippets directly (e.g. (new MySnippet).thatSnippet(nodes)), though if
>> you use any stateful stuff in those snippets that depends on the
>> current expansion point (e.g. S.attr) then you'd need to break the
>> snippet into two pieces, e.g.
>>
>> class MySnippet extends DispatchSnippet {
>> val dispatch: DispatchIt = {
>> case "foobar" => foobar
>> }
>>
>> def foobar(ns: NodeSeq): NodeSeq = {
>> S.attr("itemId").map(lookupItemById _) match {
>> case Full(item) => foobar(item)(ns)
>> case _ => error("could not find item -- itemId attribute
>> not given or invalid")
>> }
>> }
>>
>> def foobar(item: ItemModel)(ns: NodeSeq): NodeSeq =
>> bind(...)
>>
>> }
>>
>> Now if you're calling from template, you say <lift:MySnippet.foobar
>> itemId="1234">...</lift:MySnippet.foobar>, but if you're in some other
>> snippet you say MySnippet.foobar(theItem)(...)
>>
>> Hope that helps,
>> -Ross
>>
>> On Dec 23, 2009, at 4:32 PM, Alex Black wrote:
>>
>>> Thanks Ross, I think I get that.. I'm not seeing the full picture yet,
>>> let me show a bit more of my thinking:
>>
>>> So, extending my previous example, I'd like to write it like this:
>>
>>> <h1>Items</h1>
>>
>>> <lift:Item.firstAndSecond>
>>> <h2><item:which></h2>
>>> <ul>
>>> <lift:Item.points>
>>> <li><point:name />
>>> </lift:Item.points>
>>> </ul>
>>> <lift:item.picture />
>>> </lift:Item.firstAndSecond>
>>
>>> // Emit the block twice, once for each item (first,second).
>>> def firstAndSecond(xhtml: NodeSeq): NodeSeq = {
>>> List(first,second).flatMap( item => renderItem(xhtml, item) )
>>> }
>>
>>> How then do I implement Item.picture, Item.points to know *which* item
>>> is currently being rendered?
>>
>>> thx
>>
>>> - Alex
>>
>>> On Dec 23, 4:18 pm, Ross Mellgren <[email protected]> wrote:
>>>> S.attr gives you attributes of the current snippet calling tag, e.g.
>>
>>>> class Item extends DispatchSnippet {
>>>> val dispatch: DispatchIt = {
>>>> case "points" => points
>>>> }
>>
>>>> def points(ns: NodeSeq): NodeSeq = {
>>>> val points = S.attr("item") match {
>>>> case Full("first") => loadFirstListOfPoints()
>>>> case Full("second") => loadSecondListOfPoints()
>>>> case _ => error("missing item attribute or has incorrect
>>>> value")
>>>> }
>>
>>>> bind(...)
>>>> }
>>
>>>> }
>>
>>>> -Ross
>>
>>>> On Dec 23, 2009, at 4:07 PM, Alex Black wrote:
>>
>>>>> I've got some xhtml blocks in one of my templates that are basically
>>>>> identical, and I'd like to avoid the duplication, by either writing
>>>>> the block once in my template and using a snippet to write it out
>>>>> twice (with different values) or put it in its own template.
>>
>>>>> <h1>Items</h1>
>>
>>>>> <h2>First</h2>
>>>>> <ul>
>>>>> <lift:Item.firstPoints>
>>>>> <li><point:name />
>>>>> </lift:Item.firstPoints>
>>>>> </ul>
>>>>> <lift:item.firstPicture />
>>
>>>>> <h2>Second</h2>
>>>>> <ul>
>>>>> <lift:Item.secondPoints>
>>>>> <li><point:name />
>>>>> </lift:Item.secondPoints>
>>>>> </ul>
>>>>> <lift:item.secondPicture />
>>
>>>>> My question is: If I write this block once, and surround it in a
>>>>> snippet which emits it twice, how can the snippet parameterize the
>>>>> nested snippet calls?
>>
>>>>> I realize I could bind the entire block with one snippet call, but
>>>>> because my real blocks are big and complex I'd like to keep the many
>>>>> snippet calls per block.
>>
>>>>> So, more specifically, how can i write <lift:Item.secondPoints> and
>>>>> <lift:item.firstPoints> more like this: <lift:item.points
>>>>> item="@curItem">
>>
>>>>> thx
>>
>>>>> - Alex
>>
>>>>> --
>>
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "Lift" group.
>>>>> To post to this group, send email to [email protected].
>>>>> To unsubscribe from this group, send email to
>>>>> [email protected]
>>>>> .
>>>>> For more options, visit this group
>>>>> athttp://groups.google.com/group/liftweb?hl=en
>>>>> .
>>
>>> --
>>
>>> You received this message because you are subscribed to the Google
>>> Groups "Lift" group.
>>> To post to this group, send email to [email protected].
>>> To unsubscribe from this group, send email to
>>> [email protected]
>>> .
>>> For more options, visit this group
>>> athttp://groups.google.com/group/liftweb?hl=en
>>> .
>
> --
>
> You received this message because you are subscribed to the Google Groups
> "Lift" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/liftweb?hl=en.
>
>
--
You received this message because you are subscribed to the Google Groups
"Lift" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/liftweb?hl=en.