Yeah that looks like exactly what I'm looking for. I'm just debating the two options: 1. Use many snippets and pass the current item down through a request var. 2. Use one snippet (composed of many internal function calls) and pass the current item down as a parameter.
I'll give both a shot, thanks for your help. Intuitively I seem to be leaning towards #1 I think for the same reason I don't normally use just one snippet call per xhtml page, instead I make several smaller snippet calls for each page. I appreciate all the ideas and advice. - Alex On Dec 23, 10:22 pm, Ross Mellgren <dri...@gmail.com> wrote: > 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,AlexBlackwrote: > > > > > 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 <dri...@gmail.com> 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,AlexBlackwrote: > > >>> 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 <dri...@gmail.com> 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,AlexBlackwrote: > > >>>>> 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 > > ... > > read more » -- You received this message because you are subscribed to the Google Groups "Lift" group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.