bsatrom / Knockout.Unobtrusive

Unobtrusive View to ViewModel bindings for KnockoutJS

Home Page:http://bsatrom.github.com/Knockout.Unobtrusive/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add support for bindings defined in template blocks

bsatrom opened this issue · comments

Given the following template block (defined in a <script> tag):

<script id='personTemplate' type='text/html'>
    ${ name } is ${ age } years old
    <button id="makeOlder">Make older</button>
</script>

and the following binding object:

var bindings = {
  click: ['makeOlder']
};

ko.unobtrusive should be able to find the button in the template block and generate the following data-bind attribute:

<script id='personTemplate' type='text/html'>
    ${ name } is ${ age } years old
    <button data-bind='click: makeOlder'>Make older</button>
</script>

I've started preliminary work on templating in my fork

Ooops. I already had some un-commited work around this as well. Checked in for reference. Not sure what I think about it. It works, but since we're now playing the string manipulation game with the template blocks, it also feels a bit messy. Would love you see how you approached it.

Hi Brandon-
I see that you are working on some support for templates. I took a look at this recently and here were the notes that I had in general about unobtrusively adding template bindings.

  • since templates are typically repeated (like for each item in an array), you would likely want to support something other than id/name. Probably class or a "data-" attribute would make sense.
  • you also could potentially use ids on the template elements and then strip them. However, in some cases (templates that don't repeat), you may not want them removed. In other cases, you would simply be trading id="firstName" with data-bind="text: firstName". Not so sure that it is worth it, although in many cases the binding would be more complex.
  • my initial experiments were based on taking the template content, loading into a DOM structure, operating on it, then writing it back. I encountered one difficulty when dealing with IE. If you were to write the script's content using the innerHTML of your modified structure, then you end up with something like this:
    <div class=firstName data-bind="text: firstName"></div>.
    Knockout has a regular expression that it uses with templates to identify the data-bind elements (https://github.com/SteveSanderson/knockout/blob/master/build/output/knockout-latest.debug.js#L1725). The 'class=firstName' (without quotes around firstName) makes the regex fail to find our data-bind. We could likely get this changed in Knockout, but you would only be able to support new versions of KO.
  • If you do not load it into a DOM structure, then you could just parse the template as a string using regex, but seems potentially error-prone to me. Something similar to Steve's regex might work, although I am not personally a regex ninja. Otherwise, searching for 'class="x"' and replacing with 'class="x" data-bind="text: firstName"' might be sufficient.
  • Just FYI - Knockout 1.3 will include a native template engine. This supports inline templates, as well as named templates inside any DOM element (not required that it is in a script tag). Here is a sample: http://jsfiddle.net/rniemeyer/ydArJ/. Just something to keep in mind. For the most part, it should only make this type of thing easier for templates that don't live in script tags.

Ryan,

Great points all, thanks! I think my initial cut need a lot more work, so this is some good stuff to keep in mind. I'll ping back if I have any questions.

And I don't know why I didn't try to just load the markup into the DOM and work with it that way. Went down the string manipulation path instead, which introduces some quirks that muddied up the source a bit. I'll try to refactor with a DOM load and see how that works out. Thanks for the warning about the IE issue.

Looked at your initial code. Seems like it would get the basic job done. Good to get something out there to work against. Would be nice if it didn't have to process all script tags again and again for each id. The issue of id vs. class vs. another option would be something to consider. Good start!

created a dom-templating branch where I started doing the processing of templates by loading them into the DOM. It's nearly there, the processing is working, but I've got something running back through and reverting my changes. Taking a break from it for a bit and will look again later.

If you need any clues I've got template parsing done in here my preparser - https://github.com/aaronpowell/KnockoutJS-Pre-parser

@aaronpowell - I tried yours out quick to see if it was able to get past the IE < 9 issue that I mentioned above where KO would fail to find bindings, because the rewritten template would have <div class=blah data-bind="text: name"></div>. I suspect that it will, but it seems to have an issue prior to that rewriting the template.

Here is a sample: http://jsfiddle.net/rniemeyer/fF9nu/ that is failing on IE8. I would be happy to log it on your project's page as an issue, just seemed relevant in the context of this conversation. Digging into it a bit further right now...

IE9 blocks scripts that are not served with the right mime type, so I just committed your latest build to my personal repo to reference it in the fiddle.

Thanks.

if its a problem with my preparser can you raise an issue there

ok gents.. I think I got it sorted with template bindings, assuming the tests are correct. To review:

  • Implemented DOM loading on template blocks defined in script
  • Support class and id values
  • All tests pass in Chrome, IE9, 8 and 7 (Ryan, can you verify the issue you saw with IE8?)

This should also support arbitrary templates in 1.3.

Checked into master. Closing for now...

Tried something basic to test the attribute issue in a few different browsers: http://jsfiddle.net/rniemeyer/xvbH6/. Looks like the template content ends up escaped after it is processed by the unobtrusive plugin.

Looks like the bindings are still here from aaron's preprocessor plugin. Have you tried it explicitly with ko.unobtrusive? I'll fork and try it out...

-----Original Message-----
From: rniemeyer [mailto:reply@reply.github.com]
Sent: Sunday, August 21, 2011 7:14 PM
To: Brandon Satrom
Subject: Re: [Knockout.Unobtrusive] Add support for bindings defined in template blocks (#2)

Tried something basic to test the attribute issue in a few different browsers: http://jsfiddle.net/rniemeyer/xvbH6/. Looks like the template content ends up escaped after it is processed by the unobtrusive plugin.

Reply to this email directly or view it on GitHub:
#2 (comment)

Oops, didn't notice that on the UL. His script was not referenced. I updated it with the right binding on the outer element. Seems to still have the original "class=blah" issue that I had originally mentioned as a challenge.

right-o. Finally with you on this one. Will open an issue and fix now.

ok, I believe I have this fixed in the latest push. Ryan, can you verify? IE is being weird with me and JSFiddle.

Looks good!

Music to my ears! Thanks for verifying, Ryan. And for your help. Will keep on fleshing this out, but let me know if there's anything else we can/should add to make this useful for folks.

There is some stuff coming in 1.3 that will likely be of much interest to you for this plugin.

Take a look at this fiddle: http://jsfiddle.net/rniemeyer/5wAYY/

This shows:
1- a new hook provided for a function to run each time that ko.applyBindings is called
2- a way to attach bindings without using data-bind attributes (this is still subject to change a bit)
3- a way to retrieve the binding context from an element, which allows you to do things like use unobtrusive event handlers that can tap into the data that would have been available to bind against the element.

Not sure where you will go with these, but they do open up lots of possibilities. I could see you hooking into applyBindings, applying all of the bindings using the domData method, and possibly having options to use event delegation for event bindings.