Re: [Lift] More on bind and preserving attributes

2009-11-29 Thread Ross Mellgren

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

2009-11-29 Thread Jeppe Nejsum Madsen
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

2009-11-28 Thread Ross Mellgren
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

2009-11-28 Thread Jeppe Nejsum Madsen
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

2009-11-27 Thread Ross Mellgren
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

2009-11-27 Thread Jeppe Nejsum Madsen
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.