A way to elegantly solve current multiple-VM limitations (RFC)

170 views
Skip to first unread message

Hunter Loftis

unread,
Jan 28, 2011, 9:14:53 AM1/28/11
to KnockoutJS, st...@codeville.net
My team just started implementing our views with Knockout. So far it's
been a lovely experience, and thank you for releasing the library!

We see one big issue with KO, and I'd love your thoughts on it:

Jim - builds several templates that bind to userInfoVM
(userInfoViewModel.js)
David - builds sharingList view template, sharingListViewModel.js
I - build pageList view template, pageListViewModel.js

These all work great when we're developing separately, but once we
combine code, it all falls apart because we have, for example:

<div data-bind='template: {name: "pageListTemplate", data:
pageListVM}'></div>
<div data-bind='text: userInfoVM.full_name'></div>

ko.applyBindings(pageListVM); // errors out because
pageListVM.userInfoVM.full_name doesn't exist
ko.applyBindings(userInfoVM);


Now, I know we could applyBindings to specific DOM containers. That
leaves some issues though:

1) We now have to know all the DOM locations of the view/VM
combinations, which can be solved by something like:

$('.pageListView').each(function() { ko.applyBindings(pageListVM, $
(this)); });

2) We can't use multiple view models within one DOM container. For
example, inside the pageListView (using the pageListVM), I couldn't
say "{username}, here are all your pages:" because {username} is part
of the userInfoVM.


It seems like a good way to solve this would be an alternative
applyBindings like:

ko.applyBindings(someVM, 'namespace');

Where any data-bind attribute without a preceding 'namespace' would be
ignored, so this would be possible:

<div data-bind='template: {name: "pageListTemplate", data:
pageListVM}'></div>
<div data-bind='text: userInfoVM.full_name'></div>

ko.applyBindings(pageListVM, 'pageListVM'); // works because it
ignores userInfoVM.full_name
ko.applyBindings(userInfoVM, 'userInfoVM'); // works because it
ignores pageListVM

Thoughts?
-H

Ω Alisson

unread,
Jan 28, 2011, 9:19:51 AM1/28/11
to knock...@googlegroups.com
You could also use something like $.extend to reuse stuff

Hunter Loftis

unread,
Jan 28, 2011, 9:22:03 AM1/28/11
to KnockoutJS
Also, none of this is tested, but looking at that again I feel like
this is a better example for the #1 workaround:

$('.somethingView').get().forEach(function (el)
{ ko.applyBindings(somethingVM, el); }


On Jan 28, 9:19 am, Ω Alisson <thelinuxl...@gmail.com> wrote:
> You could also use something like $.extend to reuse stuff
>

rpn

unread,
Jan 28, 2011, 10:29:59 AM1/28/11
to KnockoutJS
Some type of namespace idea sounds very interesting long-term.

For the short-term though, would it not work for you to create a
higher level viewModel and attach your three "sub" viewModels as
properties? It would work similarly to namespaces. If you were
binding at the viewModel level, then you would have to always
reference each object via its "sub" viewModel property.

Otherwise, you can avoid this issue, by choosing to pass the actual
data to bind like: <div data-bind="template: {name:
'pageListTemplate', data: viewModel.pageListViewModel}"></div>

Would this work for you or is it not possible with your structure?

On Jan 28, 8:22 am, Hunter Loftis <hunter.lof...@gmail.com> wrote:
> Also, none of this is tested, but looking at that again I feel like
> this is a better example for the #1 workaround:
>
> $('.somethingView').get().forEach(function (el)
> { ko.applyBindings(somethingVM, el); }
>

Hunter Loftis

unread,
Jan 28, 2011, 12:31:37 PM1/28/11
to KnockoutJS
rpn,

Thanks for your feedback!

We're using the jquery selector solution right now, since the "global
ViewModel" solution won't work (app has too many components / at
different times):

All of the models don't necessarily exist at the same time. Some of
them are embedded into the page from our controller (eg userInfo),
while some are loaded on a per-page basis (eg projectList), and it
becomes a large overhead to manage all of that and ensure that
ko.applyBindings() is called after everything is available.

I was hoping someone had made a larger/complex app using KO for all
views, and had solved this already, because I think this is a big
limitation for such a deployment. Since it doesn't look like that's
been done, we'll probably fork KO and add namespacing in the next
week.

-H

mcoolin

unread,
Jan 29, 2011, 8:38:23 AM1/29/11
to KnockoutJS
Only problem with extend is that as your objects become more complex
the likelyhood of collision of properties grows. Unless some naming
conventions are followed.

On Jan 28, 9:19 am, Ω Alisson <thelinuxl...@gmail.com> wrote:
> You could also use something like $.extend to reuse stuff
>

Lance Wynn

unread,
Feb 10, 2011, 11:00:11 AM2/10/11
to KnockoutJS
Hi,
I have had a similar issue where I need to wire-up my page, and parts
of the view model may not be available. I solved it by binding to the
dom elements directly, but I never liked that solution because it
seems like a deviation from the pattern -Meaning that suddenly there
was this interim bit of code that did not fit in the view model
because it was aware of the views, and also was bound to identifiers
on the view itself, so I could not bind to 2 seperate dom elements
without some code changes to this pseudo view model layer-

I have been working on an 'each' iterator binder based on Brian Beck's
'bind' handler, (which I renamed in my project to 'dataContext') and
when I saw your post I realized I could solve my problem with a small
change to the dataContext binder that I called dynamicDataContext. I
posted the code here: https://github.com/lancewynn/My-knockout-bindings
but I don't know when it will appear...

anyhow, you can perhaps use it like this for your problem:

in the below snippit, I have a viewmodel that is an empty object, I
applyBindings on that view model. In my view I specified a div that
uses the dynamicDataContex binder. After the bindings are applied, I
can set the dynamicModel to the viewModel's dynamicProperty, and the
binder will handle everything from there. It seems like a pretty
slick solution, and seems to me that it follows the pattern a little
better. you'll still have to follow some naming conventions as far as
the property names of the dynamic properties, but it doesn't care if
you inject the additional view models before or after the initial
applyBindings call. Also, it makes the view a bit cleaner in my
opinion.

<body>
<script type="text/javascript">
$(function () {
viewModel = {
};

ko.applyBindings(viewModel);

dynamicModel = { dynamicName: 'Dynamic viewModel' };
viewModel.dynamicProperty(dynamicModel);
});

</script>

<div data-bind="dynamicDataContext: 'dynamicProperty'">
<span data-bind="text: dynamicName"></span>
</div>
</body>


second sample showing the viewmodel being applied prior to the initial
applyBindings:
<body>
<script type="text/javascript">
$(function () {
viewModel = {
};
dynamicModel = { dynamicName: 'Dynamic viewModel' };
viewModel.dynamicProperty = dynamicModel;

ko.applyBindings(viewModel);


});

</script>

<div data-bind="dynamicDataContext: 'dynamicProperty'">
<span data-bind="text: dynamicName"></span>
</div>
</body>

Ω Alisson

unread,
Feb 10, 2011, 11:04:35 AM2/10/11
to knock...@googlegroups.com
This may prove itself really nice playing with reusing server-side models like those of JPA, ActiveRecord, etc

Hunter Loftis

unread,
Apr 3, 2011, 2:15:40 PM4/3/11
to KnockoutJS
In case anybody stumbles onto this old thread, I eventually hooked
this up as a simple plugin for easy namespaced binding here:

https://github.com/hunterloftis/knockout.namespaces
Reply all
Reply to author
Forward
0 new messages