Progressive Enhancement & KnockoutJS

380 views
Skip to first unread message

tonyisgaaf

unread,
May 11, 2011, 5:47:49 AM5/11/11
to KnockoutJS
As I normally create a website / -app, I would start with the markup
and styles, make sure the "behaviour" (like adding and removing) works
with regular POSTed forms and then add client-side behaviour using JS.
I see a huge benefit in using KnockoutJS, but the first thing I
encountered in my very simple test (below), was that the results of
the viewModel are "added" to the DOM, instead of replacing the
existing values.

I can totally understand that, since KnockoutJS doesn't know what
should be cleared before adding, I should clean up the containers
before applying bindings. But still, it would be hugely beneficial if
KO would fit in more with this idea of "progressively enhancing" (the
DOM), while not having me think about preparing the DOM first.

So, this isn't really a question or a remark, but more like thinking
aloud. Tune in if you've got a better idea!

And @Steve Sanderson: I think you're a brilliant person, I bought both
of your MVC books and saw you live @CG10. Thanks for creating this
generalized solution and connecting the dots!

=== Example ===

<!DOCTYPE html">

<html lang="en">
<head>
<title>knockout-test</title>

<script src="jquery-1.6.js" type="text/javascript"
charset="utf-8"></script>
<script src="jquery.tmpl.js" type="text/javascript"
charset="utf-8"></script>
<script src="knockout-1.2.0.debug.js" type="text/javascript"
charset="utf-8"></script>
</head>
<body>
<h2>Friends</h2>
<ul data-bind="template: { name: 'friendsTemplate', foreach:
friends }">
<li>Bob</li>
<li>Fred</li>
</ul>

<script type="text/html" id="friendsTemplate">
<li>${ $data } - ${ new Date }</li>
</script>

<script type="text/javascript">
(function () {
var viewModel = {
friends: ko.observableArray(["Bob", "Fred"])
};

ko.applyBindings(viewModel);
} () );
</script>
</body>
</html>

Ω Alisson

unread,
May 11, 2011, 8:00:33 AM5/11/11
to knock...@googlegroups.com
It is really hard to do progressive enhancement with KnockoutJS

Steven Sanderson

unread,
May 11, 2011, 8:21:53 AM5/11/11
to knock...@googlegroups.com
To expand and clarify on Alisson's point, since KO is all about MVVM-in-the-browser, the scenario it targets always involves JavaScript. 

In this case, and particularly as your app behaviours get more sophisticated, this goes way beyond what's possible with progressive enhancement. In my experience, most people who want to build sophisticated behaviours quickly find that trying to structure the code in a progressive enhancement style becomes counter-productive. 

That said, if you do find scenarios where you have particular ideas of how
KO could be more helpful, please let us know. I'm not against progressive enhancement in principle; I just find it a very suboptimal pattern if you're requiring JavaScript anyway. 

In your example, to fit the normal MVVM pattern, you can send the initial model state to the client as JSON data, then have the UI generated from that. 

What do you think?
Steve

Mark Bradley

unread,
May 11, 2011, 8:49:28 AM5/11/11
to knock...@googlegroups.com
I was thinking about this recently as well. One possible way around it
would be to switch out the html-only html with the html generated by
KO. If they are equivalent and you do it in the right way there
shouldn't be any indication on the user side. I did notice in the KO
source code relating to templates that there is a an idea of a "Render
Mode" here:
https://github.com/SteveSanderson/knockout/blob/master/src/templating/templating.js#L32
but I could not find a way to manipulate this from the data-bindings.
Specifically I wanted to replace the children nodes with the output of
the template (this could be similar to what you were referring to in
your 1.3 roadmap re: inline templates).

2011/5/11 Steven Sanderson <ste...@stevensanderson.com>:

--
-barkmadley
sent from an internet enabled device

rpn

unread,
May 11, 2011, 12:22:23 PM5/11/11
to knock...@googlegroups.com
This is maybe a little bit off topic, but you can get creative with bindings to actually populate your viewModel from the existing content.

For example, you could add an "initialize" binding that populates an observable from the current field's value.  Something basic like:

//initialize an observable from current value in the field
ko.bindingHandlers.initialize {
    initfunction(elementvalueAccessorallBindingAccessorcontext{
        var observable valueAccessor();
        var value $(element).val();
        if (ko.isWriteableObservable(observable){
            observable(value);
        }
    }
};


You could even create observables on your model, if we passed a string instead of the observable to the binding.  Something like this:

//create an observable set to the current field's value.  Rewrite data-bind to use real value binding
ko.bindingHandlers.valueWithInit {
    initfunction(elementvalueAccessorallBindingsAccessorcontext{
        var property valueAccessor();
        var value $(element).val();
        
        //create the observable, if it doesn't exist
        if (!ko.isWriteableObservable(context[property]){
            context[propertyko.observable();   
        }
        context[property](value);
        
        //rewrite data-bind to use real value binding (would need to be more robust, if attribute contains additional bindings). 
        //Otherwise, we could certainly just call the value binding's init and update functions with a new valueAccessor that returns the real observable.
        $(element).attr("data-bind""value: " property );
        ko.applyBindings(contextelement);
    }
};


It would certainly be harder to get this to work with the foreach option of the template binding.  The initialize would have to know what to look for in the children and push the content to an array.

Otherwise, you could create a simple "clear" binding to remove the existing content.  Although, I don't think that it would give you much benefit.


The bindings are just a sample to demonstrate the idea and currently couldn't handle things like nested properties.




tonyisgaaf

unread,
May 11, 2011, 1:09:00 PM5/11/11
to KnockoutJS
@Steven Sanderson: "In your example, to fit the normal MVVM pattern,
you can send the initial model state to the client as JSON data, then
have the UI generated from that."

But that's exactly the point, as that wouldn't "progressively enhance"
the DOM. It's only MVVM if there's JS, otherwise it's just, well,
blank. You're absolutely right about the sophisticated behaviour being
a pain to do with POSTed forms, but I do work on a couple of mobile
web-apps, where JS is (still) not a given. And

@Mark Bradley: "If they are equivalent and you do it in the right way
there shouldn't be any indication on the user side".

Exactly, but implementing this might be harder than it looks (what is
equivalent?). On the other hand, say you have an element like in my
example (one with a bound template):

<ul data-bind="template: { name: 'friendsTemplate', foreach:
friends }">
<!-- We're going to pretend there are still people out there
with no JS! The thought! -->
<li>Bob</li>
<li>Fred</li>
</ul>

Wouldn't it be an idea to replace the contents of an element with a
bound template and regard the existing contents as fallback content? I
think it fits in neatly with the existing idea of fallback content in
HTML.

@rpn: "This is maybe a little bit off topic, but you can get creative
with bindings to actually populate your viewModel from the existing
content."

I did think of that as well, but came to the conclusion that it kind
of defeats the purpose. You lose all the good things JSON has and
introduce so many potential points of failure. I've done a fair bit of
screen-scraping (in JS!) but it's very error-prone.

I don't think creating a full HTML view and loading the initial data
(using JSON) is a problem in an MVC environment from any performance
point of view and I do still like the thought of progressive
enhancement more than depending on JS. It's a tiny bit academic,
though.

PS Sorry for responding so late, forgot to subscribe to the thread
initially.

rpn

unread,
May 11, 2011, 1:22:07 PM5/11/11
to knock...@googlegroups.com
As I mentioned, you could use a simple "clear" binding to remove the contents of your container, before the template runs.  Then, your fallback content would remain when there is no JS.  I am not saying that it solves all of your concerns, but it would be an option for your template scenario.


tonyisgaaf

unread,
May 11, 2011, 2:23:48 PM5/11/11
to KnockoutJS
Read right past the "clear binding" part. Looks promising, but I need
to dive into the bindingHandlers first to get a grip on it. Are the
bindings evaluated in the order they are declared?

rpn

unread,
May 11, 2011, 10:15:48 PM5/11/11
to knock...@googlegroups.com
I have never seen the bindings run out-of-order, but I do recall Steve saying that the order is not guaranteed, just that they they generally are evaluated left-to-right.

So, while you would likely not see any issue placing the "clear" binding first, a failsafe way to do this would be to create a wrapper to the template binding that also does a clear.  Something like:

ko.bindingHandlers.templateWithClear {
    initfunction(element{
        $(element).empty();    
        //template binding has no init to call
    },
    updateko.bindingHandlers.template.update
};

codedat...@gmail.com

unread,
Aug 25, 2013, 4:01:53 AM8/25/13
to knock...@googlegroups.com, thijs....@gmail.com
I offered a template binding that uses the generated HTML from the server. It is similar to the original Template binding, except for the few data-attributes that will be used by the binding. I like to extend the work, so please in case of any suggestion, let me know. You can find it here.

tysonca...@gmail.com

unread,
Jul 28, 2014, 10:20:22 AM7/28/14
to knock...@googlegroups.com, thijs....@gmail.com
I have outline one approach here: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive-enhancement-on-lists

The basic idea is that the list is rendered by the server, and then we use a Knockout custom binding to re-render it. The markup ends up looking like this:

<ul data-bind='initialForEach: { template: forEachTemplate }, foreach: animals'> <li data-options='{ "id": 1, "type": "Mammal", "name": "Dog" }'>Dog</li> <li data-options='{ "id": 2, "type": "Mammal", "name": "Cat" }'>Cat</li> <li data-options='{ "id": 3, "type": "Fish", "name": "Goldfish" }'>Goldfish</li> <li data-options='{ "id": 5, "type": "Bird", "name": "Chicken" }'>Chicken</li> </ul>

While not the most ideal solution, I haven't seen anything better out there.
Reply all
Reply to author
Forward
0 new messages