MemoizeTransforms that binds the outer elem that's memo:ed.

50 views
Skip to first unread message

Viktor Hedefalk

unread,
Apr 17, 2013, 8:30:01 AM4/17/13
to liftweb
Hi,

I seem to have forgotten a bit about the idMemoize in SHtml. I have this:

<div data-lift="Member.memos">
<div data-lift-id="inner">MemoA</div>
<div class="clearable">MemoB</div>
<div class="clearable">MemoC</div>
</div>

<div data-lift="Member.nomemos">
<div data-lift-id="inner">NoMemoA</div>
<div class="clearable">NoMemoB</div>
<div class="clearable">NoMemoC</div>
</div>


and the snippets that only differ that one of them binds with a Seq of
MemoizeTransforms and the other with straight CssSel:

def memos = ClearClearable andThen "data-lift-id=inner" #> (1 to
3).map(i => SHtml.idMemoize(_ => "* *+" #> i))
def nomemos = ClearClearable andThen "data-lift-id=inner" #> (1 to
3).map(i => "* *+" #> i)


<div>
<div data-lift-id="inner" id="F679456627442NSUPJ1">MemoA</div>
<div data-lift-id="inner" id="F679456627443XWGHKZ">MemoA</div>
<div data-lift-id="inner" id="F679456627444XHV3YD">MemoA</div>
</div>

<div>
<div data-lift-id="inner">NoMemoA1</div>
<div data-lift-id="inner">NoMemoA2</div>
<div data-lift-id="inner">NoMemoA3</div>
</div>


My expectation was that the binding would look the same. But idMemoize
only binds on the children of the element that the actual memo-id is
applied. It feels like I need something that binds to the actual
element that is being memoized and not just its children.

My particular use case is a tabbed panel using Bootstrap. I need to
rerender some or all of them when stuff happens. And I need to set
attributes on the actual tab-pane elements (at least at page load)
since this is whats determining if they are visible or not. Something
like

.tab-content > .tab-pane { display: hidden;}
.tab-pane .active { display: block;}


In code, my problem is that the bind function is only applied to the children:

SHtml.idMemoize:

def applyAgain(): NodeSeq =
new Elem(latestElem.prefix,
latestElem.label,
latestElem.attributes,
latestElem.scope,
f(this)(latestKids) :_*)

def setHtml(): JsCmd = SetHtml(latestId, f(this)(latestKids))



Since I want to memoize on the .tab-pane:s themselves I can't bind
attributes on this outer element. I tried just to surround with an
simple container-div to memo on but this kind of broke the
bootstrap-stuff.

Of course all of this is fixable with html/css, but I'm really trying
to approach this project with an extreme "don't touch ANYTHING in
html/css just for binding"-approach. Going the whole nine yards with
designer-friendliness :)


Any recommendations highly appreciated!

/v

Viktor Hedefalk

unread,
Apr 17, 2013, 9:11:44 AM4/17/13
to liftweb
Turns out it was pretty damn fast and easy to make a modified
idMemoize that did what I needed. Maybe this could be helpful for
someone else. I haven't tested it thoroughly yet but it seems to work
fine. I'll be back if it turns out broken.

Please note that I had to resolve to JQuery-specific commands to do
the replace. I think replace is not in the common Js-stuff right?
Anyway, since I did that I also replaced the id:s with data-attributes
so I could use the id:s for other stuff (remember - I'm really picky
about not messing with the markup/styling)

Anyway - just gotta say: I love Lift :)

There are so many great ideas from David and others in here and in the
rare occasion it doesn't fit my specific needs It's so damn easy to
just look at the source and modify until it does. There's ALWAYS
options and I haven't one single time felt cornered.


To recap about this memo-function - the difference here is that the
binding function is applied to the whole thing and not just the
children. So if placed next to a pure CssSel like above it will do the
same thing. Still got to think about binding to a single elem though.
That could probably be fixed too…


def idMemoize(f: MemoTransform => NodeSeqFuncOrSeqNodeSeqFunc):
MemoTransform = {
new MemoTransform {
var tmpl: Elem = <span/>

var latestId = Helpers.nextFuncName

private def fixElem(e: Elem): Elem = {
e.attribute("data-lift-memo-id") match {
case Some(id) =>
latestId = id.text; e
case None => e % ("data-lift-memo-id" -> latestId)
}
}

def apply(ns: NodeSeq): NodeSeq =
Helpers.findBox(ns) { e => tmpl = fixElem(e); Full(e) }.
map(ignore => applyAgain()).openOr(NodeSeq.Empty)

def applyAgain(): NodeSeq = f(this)(tmpl)
def setHtml(): JsCmd = (JqMemoId(latestId) ~>
JqJE.JqReplace(applyAgain)).cmd

}
}

trait MemoTransform extends (NodeSeq => NodeSeq) {
def applyAgain(): NodeSeq
def setHtml(): JsCmd
}

case class JqMemoId(id: String) extends JsExp {
override def toJsCmd = """jQuery('[data-lift-memo-id="""" + id + """"]')"""
}


Thanks,
Viktor

Aleh Aleshka

unread,
May 13, 2015, 10:36:23 AM5/13/15
to lif...@googlegroups.com
I've stumbled upon this myself.
There are two issues with this, as far as i can see - you can't modify outer element from idMemoize, and jquery html is quite a bit slower then replaceWith https://jsperf.com/jquery-html-vs-replacewith
So, is there a reason why idMemoize uses SetHtml instead of Replace by default?
Thanks, Aleh

Aleh Aleshka

unread,
May 13, 2015, 11:12:25 AM5/13/15
to lif...@googlegroups.com
Scratch the part about performance, html and replaceWith are actually identical http://jsperf.com/jquery-html-vs-replacewith/13
The part about outer element is still pretty major, though. I've spent quite a bit of time trying to understand why my css selectors where not working only to find out that outer element was ignore..
Reply all
Reply to author
Forward
0 new messages