Re: [Lift] More on bind and preserving attributes
On Nov 29, 2009, at 5:54 AM, Jeppe Nejsum Madsen wrote: > Ross Mellgren writes: > > Very nice! I'll try to play with this and see how it works out. > > I think it serves the purpose well: For single element bind use -%%>, > for the rest use ->. > > Now, is there a reason this functionality couldn't be folded into the -> > operator? Granted, the prefix:attrs syntax may be too much magic? It's quite a bit of magic, and also it requires going through the result elements twice (once to splice in the new attributes, and again when snippet processing recurses) which would just be wasted if you're not doing this type of bind. Those would be my reasons to have a switchable behavior rather than the default. -Ross -- 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.
Re: [Lift] More on bind and preserving attributes
Ross Mellgren writes: > Okay, I wrote an implicit + operator combo that scraps most of the > boilerplate. > > Snippet code: > > def howdy(in: NodeSeq): NodeSeq = > bind("foobar", in, "foobaz" -%%> FocusOnLoad( name="username" id="email" class="text" input:attrs="" />)) > > (input:attrs="" is the magic) > > Template code: > > > > > > Output: > /> > // jQuery(document).ready(function() > {document.getElementById("email").focus();}); > // ]]> > > > The -%%> operator will scan all the nodes on the right hand side looking for > a special prefix:attrs attribute, which it will replace with all the prefixed > attributes from the template that start with the prefix, combining style and > class attributes as before. This gives you the same flexibility as %% > currentPrefixedAttrs with somewhat less typing (and more magic), plus you can > attach attributes to different nodes: > > def howdy(in: NodeSeq): NodeSeq = > bind("foobar", in, "foobaz" -%%> ( id="email" class="text" username:attrs="" /> > id="email" class="text" password:attrs="" />)) > > Note that I wrote it to scan only shallowly -- if you have a nested output it > will only look at the outermost elements. A recursive version would probably > be trivial. > > I posted the entire code to git so you can take a look -- > http://www.github.com/dridus/test-bindparams > > The heart of the code is > http://github.com/Dridus/test-bindparams/blob/master/src/main/scala/test/lib/BindParamExtensions.scala Very nice! I'll try to play with this and see how it works out. I think it serves the purpose well: For single element bind use -%%>, for the rest use ->. Now, is there a reason this functionality couldn't be folded into the -> operator? Granted, the prefix:attrs syntax may be too much magic? /Jeppe -- 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.
Re: [Lift] More on bind and preserving attributes
Okay, I wrote an implicit + operator combo that scraps most of the boilerplate. Snippet code: def howdy(in: NodeSeq): NodeSeq = bind("foobar", in, "foobaz" -%%> FocusOnLoad()) (input:attrs="" is the magic) Template code: Output: // The -%%> operator will scan all the nodes on the right hand side looking for a special prefix:attrs attribute, which it will replace with all the prefixed attributes from the template that start with the prefix, combining style and class attributes as before. This gives you the same flexibility as %% currentPrefixedAttrs with somewhat less typing (and more magic), plus you can attach attributes to different nodes: def howdy(in: NodeSeq): NodeSeq = bind("foobar", in, "foobaz" -%%> ( )) Note that I wrote it to scan only shallowly -- if you have a nested output it will only look at the outermost elements. A recursive version would probably be trivial. I posted the entire code to git so you can take a look -- http://www.github.com/dridus/test-bindparams The heart of the code is http://github.com/Dridus/test-bindparams/blob/master/src/main/scala/test/lib/BindParamExtensions.scala -Ross On Nov 28, 2009, at 9:24 AM, Jeppe Nejsum Madsen wrote: > Ross Mellgren writes: > >> I'm not sure if this addresses your points, but I figured I'd throw >> out what I've been doing in case it helps. > > It's always nice to see how others approach a problem! > >> I don't use -%>, but I do use something similar along with another >> implicit so I can place the attrs in exactly the right place, and it >> automagically combines class and style attributes correctly. Some >> example code: >> >> bind("user", loginXhtml", >> "email" -> (FocusOnLoad(> id="email" class="text" /> %% currentPrefixedAttrs("input")), >> ...) > > Much cleaner than my version :-) > >> I think personally that the default behavior of bind is correct and >> least surprising, since (at least for me and the applications I have) >> it's very common that a single bound tag does not map one to one to a >> result elem. > > I don't think I disagree that the current bind is very clean and easy to > understand. I guess the bind use cases can be divided in two: > > 1) The bound element is (conceptually) replaced with a single element > e.g an input field. I think the example above falls in this category > even if it outputs several elements (ie input, script etc) > > 2) The bound element is replaced with a more complex NodeSeq. > > The current bind works fine for 2), but 1) I think can be made > easier. Your approach is a step in the right direction, but I still feel > there's too much boilerplate. Basically it should be natural to always > use this approach whenever a single element is (conceptually) > output. That way it's easy to add/change attributes in the template > afterwards without changing the bind code. > > /Jeppe > > > [...] > > -- > > 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. > > -- 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.
Re: [Lift] More on bind and preserving attributes
Ross Mellgren writes: > I'm not sure if this addresses your points, but I figured I'd throw > out what I've been doing in case it helps. It's always nice to see how others approach a problem! > I don't use -%>, but I do use something similar along with another > implicit so I can place the attrs in exactly the right place, and it > automagically combines class and style attributes correctly. Some > example code: > > bind("user", loginXhtml", > "email" -> (FocusOnLoad( id="email" class="text" /> %% currentPrefixedAttrs("input")), > ...) Much cleaner than my version :-) > I think personally that the default behavior of bind is correct and > least surprising, since (at least for me and the applications I have) > it's very common that a single bound tag does not map one to one to a > result elem. I don't think I disagree that the current bind is very clean and easy to understand. I guess the bind use cases can be divided in two: 1) The bound element is (conceptually) replaced with a single element e.g an input field. I think the example above falls in this category even if it outputs several elements (ie input, script etc) 2) The bound element is replaced with a more complex NodeSeq. The current bind works fine for 2), but 1) I think can be made easier. Your approach is a step in the right direction, but I still feel there's too much boilerplate. Basically it should be natural to always use this approach whenever a single element is (conceptually) output. That way it's easy to add/change attributes in the template afterwards without changing the bind code. /Jeppe [...] -- 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.
Re: [Lift] More on bind and preserving attributes
I'm not sure if this addresses your points, but I figured I'd throw out what I've been doing in case it helps. I don't use -%>, but I do use something similar along with another implicit so I can place the attrs in exactly the right place, and it automagically combines class and style attributes correctly. Some example code: bind("user", loginXhtml", "email" -> (FocusOnLoad( %% currentPrefixedAttrs("input")), ...) The %% is a method on an extension of Elem which knows how to combine MetaData like %, but properly handles combining style= and class= attributes. currentPrefixedAttrs is a helper method that collects all the attributes of the current binding node with a given prefix and strips the prefix, basically like S.prefixedAttrsToMetaData, but for the current binding node instead of the current snippet node. This is fairly brief, does not have the same "which Elem" problems that -%> might since you specifically annotate where you want the attributes to go, does not have the problem with class= and style= attributes. I think personally that the default behavior of bind is correct and least surprising, since (at least for me and the applications I have) it's very common that a single bound tag does not map one to one to a result elem. I'm not sure what the general policy on utility-style stuff like this, but if there's a desire, I can contribute it into the lift codebase. Here's currentPrefixedAttrs and it's helpers: object XMLHelpers { /** Transform all attributes of a element with the given prefix to unprefixed attributes with the same values, discarding any other attributes */ def prefixedAttrsToUnprefixed(prefix: String)(attrs: MetaData): MetaData = stripPrefixes(attrs.filter(attrsWithPrefix(prefix))) /** Strip prefixes from prefixed attributes, leaving unprefixed attributes */ def stripPrefixes(in: MetaData): MetaData = in.foldLeft[MetaData] (Null) { (rest, md) => md match { case pa: PrefixedAttribute => new UnprefixedAttribute (pa.key, pa.value, rest) case upa: UnprefixedAttribute => new UnprefixedAttribute (upa.key, upa.value, rest) case Null => rest case x => throw new IllegalArgumentException("Not sure how to process " + x + " (of type " + x.getClass.getName + ")") } } /** A filtering function which matches any prefixed attribute with a given prefix */ def attrsWithPrefix(pre: String)(md: MetaData): Boolean = md match { case pa: PrefixedAttribute if pa.pre == pre => true case _ => false } /** Implicitly convert an Elem to a ElemExtension to enable additional functionality */ implicit def elemToElemExtension(e: Elem): ElemExtension = ElemExtension(e) /** Like {...@link MetaData#update} but instead of overwriting class and style attributes, the values are combined with an appropriate format for those attributes. Other attributes are overwrriten per usual. */ def combineMetaData(md1: MetaData, md2: MetaData, scope: NamespaceBinding): MetaData = { def notClassNorStyle(md: MetaData) = md.key match { case ("class"|"style") => false; case _ => true } def classAttr(tail: MetaData) = (md1("class"), md2("class")) match { case (null, null) => tail case (a , null) => new UnprefixedAttribute("class", Text (a.text), tail) case (null, b ) => new UnprefixedAttribute("class", Text (b.text), tail) case (a , b ) => new UnprefixedAttribute("class", Text (a.text + " " + b.text), tail) } def styleAttr(tail: MetaData) = (md1("style"), md2("style")) match { case (null, null) => tail case (a , null) => new UnprefixedAttribute("style", Text (a.text), tail) case (null, b ) => new UnprefixedAttribute("style", Text (b.text), tail) case (a , b ) => new UnprefixedAttribute("style", Text (a.text + "; " + b.text), tail) } MetaData.update(md1.filter(notClassNorStyle), scope, classAttr (styleAttr(md2.filter(notClassNorStyle } } /** Extension on Elem that adds helpful behavior */ case class ElemExtension(e: Elem) { import XMLHelpers.combineMetaData /** Generate a new Elem with the given additional metadata like Elem's % operator, but uses {...@link XMLHelpers#combineMetaData} instead of {...@link MetaData#update} */ def %%(updates: MetaData): Elem = Elem(e.prefix, e.label, combineMetaData(e.attributes, updates, e.scope), e.scope, e.child: _*) } -Ross On Nov 27, 2009, at 5:05 AM, Jeppe Nejsum Madsen wrote: > Hi, > > I know this has been discussed somewhat at length (e.g. here > http://groups.google.com/group/liftweb/browse_thread/thread/27aed5f45faf759/a261a2dfe0ae7207?lnk=gst&q=attributes+bind#a261a2dfe0ae7207 > > ) > > but I still think the default behaviour of -> should b
[Lift] More on bind and preserving attributes
Hi, I know this has been discussed somewhat at length (e.g. here http://groups.google.com/group/liftweb/browse_thread/thread/27aed5f45faf759/a261a2dfe0ae7207?lnk=gst&q=attributes+bind#a261a2dfe0ae7207) but I still think the default behaviour of -> should be reconsidered if at all possible. Here's why: I went through our app fixing small UI annoyances. One is missing tab index on input fields. I knew this has been working previously. Template: {S.??("email.address")} {S.??("password")} ({S.??("recover.password")}) Code: bind("user", loginXhtml, "email" -> (FocusOnLoad()), "password" -> (), "submit" -> ()) Note the user:tabindex attribute which used to emit a tabindex attribute in the HTML (this has been changed cf. the conversation above) I can now change user:tabindex to tabindex and -> to -%> and I get the tabindex parameter emitted. There are a few problems with this: 1) It doesn't work for the email field since it doesn't bind an Elem but a NodeSeq (so -%> wouldn't now where to attach the attributes. You could attach the attributes to the first elem in the NodeSeq (which would work here), but this doesn't sound like a nice general solution) 2) I have to change my code if I need to introduce attributes on the rendered elem (ignore the fact that my template code already contains a class attr :-) 3) To change the email field, I came up with this which seems rather verbose, but perhaps there's a better way? "email" -> {_:NodeSeq => val n = BindHelpers.currentNode openOr scala.xml.NodeSeq.Empty (FocusOnLoad(mixinAttributes ()(n).first.asInstanceOf[Elem]))}, Because of 2) I'm tempted to always use -%> in bind. I would like UI info to be editable by our UI guy without having to change code when introducing new attributes. But If I use -%> always, it might as well be the default mode of operation :-) But maybe I'm the only person with such needs? Imo, it boils down to the fact that I frequently need to specify attributes in the markup code but have to either 1) anticipate this up front in the binding or 2) Change both markup and code when a change is introduced. I'm not sure what the best solution is but to me it looks like the problem that needs solving is where to attach the attributes specified in the markup? Before trying to solve this I'm interested in gauging the possibility of changing this at all :-) /Jeppe -- 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.