New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components and custom elements #1273

Closed
SteveSanderson opened this Issue Jan 11, 2014 · 74 comments

Comments

Projects
None yet
@SteveSanderson
Contributor

SteveSanderson commented Jan 11, 2014

We're typically quite conservative about adding significant new features to Knockout core, since we highly value keeping it as a small focused library, and not a big heavy framework.

This is good, but web development in 2014 as a whole is now converging on a newer way of building applications. Inspired by future standards including Web Components (with HTML imports and custom elements), plus experimental libraries such as Polymer.js, you can see that developers will soon want and expect to build highly modular applications, composed from reusable encapsulated pieces, with extremely clean markup.

Knockout in 2014

How should Knockout.js respond? One possible response would be to do nothing, and say that people targetting really new browsers can find their own ways of combining those new standards with KO. Possibly using polyfills, even though it's hard work. But if we do that then I think the relevance of Knockout will decline.

A different response, which I favour, is to embrace this direction and provide really first-class support for 2014-style web development in Knockout by means of a few carefully-chosen core features.

And among all the major JavaScript libraries, Knockout is probably the only one that can deliver this in a way that works seamlessly right back to IE 6, because KO already has exceptional backward compatibility.

Proposed new features

I've given this a fair bit of thought and built some prototypes, and here are the three interrelated features that I think are sufficient and necessary:

  1. Components - an easy way for your view to asynchronously pull in separate things that encapsulate their own viewmodel and view
  2. Custom elements - a simple and satisfying syntax for referencing components without all the data-bind
  3. Text interpolation - a.k.a., {{ curly }} expressions inside text and element attributes

These things don't turn Knockout into a framework - it's still a library - but they are hugely flexible tools that encourage and support more modular architectures.

Prototype: More about this later, but if you're impatient, here it is.

Components

A component is a combination of view and viewmodel. You register a component with some name before you can use it:

ko.components.register("cart-summary", {
    template: ..., // Element name, or source URL, or a function that asynchronously returns a node array,
    viewModel: ... // Object, or AMD module name, or a function that asynchronously returns an object
})

Now you wouldn't often use the component binding directly (since you'd probably use a custom element instead - see below), but if you wanted to:

<!-- ko component: { name: "cart-summary", data: someData } --><!-- /ko -->

Of course, that looks ugly, hence the custom elements feature below. But what this does is pass the someData value to the template and viewModel callbacks for the component, then when it's got the results, renders the template with the viewmodel. Simple, but also powerful.

Implementation

This is pretty much just a variation on the template binding, except it supports retrieving both a view and a viewmodel asynchronously before it renders. We could even implement it by extending template, but I suspect it might be cleaner to keep it separate.

Custom elements

For a nice way of consuming a component, use custom elements:

<cart-summary user="{{ currentUser }}" max-items="10"></cart-summary>

Given that you've registered a component named cart-summary, this just works. The data value it passes to the underlying component in this case is { user: currentUser, maxItems: 10 }.

And here's the whole point of this: now your application doesn't have to be a monolith any more. Instead of one master viewmodel that is directly coupled to child viewmodels which are directly coupled to more child viewmodels, i.e., a monolith, you can now instead have many completely independent viewmodels that have no knowledge of each other, and exchange data only via custom elements in their views.

This makes code easier to reason about, and pieces easier to reuse. And if people build really general-purpose components (grids, etc.), they can be packaged as independent open source projects.

Implementation

In case you're wondering about the {{ ... }} syntax, and how it's possible for components to know when the values change (and write back changes) even if they aren't just observable instances, well that actually works out quite nicely. KO knows when evaluating them whether they have any dependencies, and hence might change in the future, and in that case can wrap the accessors in writable computeds. Sounds complex but the result is that it just does what you'd want and expect.

Text interpolation

This is way overdue. In a sense it's separate from components and custom elements, but the lack of this feature in core is kind of embarassing already.

It just means this:

<a data-bind="attr: { href: '/items/' + id() }">
    See also: <span data-bind="text: name"></span>
</a>

... can be written:

<a href="/items/{{ id }}">See also: {{ name }}</a>

That's a bit better :)

Having this just means that all your components' views become not annoying at all. It does not eliminate data-bind; it just makes it unnecessary for inserting text and setting simple, write-only attributes.

Implementation

We could already do this easily as a node preprocessor. However since this involves walking the text inside every attribute inside every element, performance is pretty vital. We probably want to be sure that we only go looking for the curly braces once per DOM fragment, not every time the same DOM fragment is inserted into the document (e.g., in a foreach). Maybe this fits into "template rewriting", but I haven't actually dug into this yet. I'd be especially interested in whether @mbest has ideas for making this super-fast.

And BTW - the curly braces used for custom element parameters would be implemented separately, because on custom elements you're not just assigning attributes.

A prototype

I've spiked up rough versions of components and custom elements here: https://github.com/SteveSanderson/ko-custom-elems-test/. There's a live demo too, though the end result is not as interesting as the code.

This proof-of-concept includes:

  • Incremental loading. When you first arrive, it only loads code related to what's on the screen. As you navigate around, it automatically pulls in any additional JS/HTML needed for what you're seeing. This is thanks to components supporting asynchronous loading. The actual JS-file-loading works via require.js in this demo, but you could also use any other way of fetching viewmodels.
  • Routing via crossroads.js. I'm not proposing this somehow becomes part of KO; I'm just showing it works cleanly with components.
  • Browser support right back to IE 6, even with all the custom elements

For some of the APIs, I've put in a lot of thought already. For other APIs, it's pretty arbitrary. All of the implementation is rough. No doubt we (and especially Michael) can come up with more streamlined implementations.

This prototype does not yet include text interpolation. It would be easy to add it in a simple way, but I'm interested in nailing the performance on that first.

Why not a plugin?

We could totally do this as a plugin. That might even be a good way to start.

However, the goal here is not just to provide some new features that some people might like. It's about aligning the Knockout project with the direction of the industry as a whole, making the core library relevant and appealing to web developers in the coming years.

Feedback requested

Please let me know what you think!

Please try to keep replies focused on the features described here. For discussion of any other possible features (or existing bugs, etc.), many other great GitHub issues are available :)

@jakcharlton

This comment has been minimized.

Show comment
Hide comment
@jakcharlton

jakcharlton Jan 11, 2014

OK - I like it ...

Halfway through a Knockout project for which Angular was proving a little too restrictive ... and I have ended up writing code to do a lot of what is being proposed here (though not inside Knockout but as wrappers around other things)

OK - I like it ...

Halfway through a Knockout project for which Angular was proving a little too restrictive ... and I have ended up writing code to do a lot of what is being proposed here (though not inside Knockout but as wrappers around other things)

@AlexZeitler

This comment has been minimized.

Show comment
Hide comment

I like it

@thunklife

This comment has been minimized.

Show comment
Hide comment
@thunklife

thunklife Jan 11, 2014

+1 for text/string interpolation

Meh on the components.

I think you said it best, "[t]his is pretty much just a variation on the template binding" I've found that template more than fits my needs; adding async loading to that would be a win. For me, templates became infinitely more powerful when I realized that enable you to build components; I just had to change my thinking of what a template is. I agree the benefit of custom elements would be nice because it makes things explicit; I just don't think I need it.

+1 for text/string interpolation

Meh on the components.

I think you said it best, "[t]his is pretty much just a variation on the template binding" I've found that template more than fits my needs; adding async loading to that would be a win. For me, templates became infinitely more powerful when I realized that enable you to build components; I just had to change my thinking of what a template is. I agree the benefit of custom elements would be nice because it makes things explicit; I just don't think I need it.

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jan 11, 2014

Contributor

@wilhelmson - it sounds like you're saying that components are useful, because you've been building them using template. As an advanced KO developer you may have been able to figure out how to do it on your own (without the async?), but a great majority of newcomers would probably benefit from a streamlined, built-in system.

Contributor

SteveSanderson commented Jan 11, 2014

@wilhelmson - it sounds like you're saying that components are useful, because you've been building them using template. As an advanced KO developer you may have been able to figure out how to do it on your own (without the async?), but a great majority of newcomers would probably benefit from a streamlined, built-in system.

@chanan

This comment has been minimized.

Show comment
Hide comment
@chanan

chanan Jan 11, 2014

Very nice. I already use Durandal for views and viewmodels, but adding it to KO would be nice. As well as the syntax for binding without the data-bind.

One thing that you may or might not be able to solve, but I struggle with, is routing with cascading form fields. My code always comes out ugly. So if I have a countries and states drop down and i want my url to be /mypage/usa/ny - and have that url load the proper settings in the drop down when the user goes to it. Its never pretty.

As I said, I am not sure of these updates will help with that and of course you don't want to add routing to KO, but maybe somehow the components can be routing aware.

chanan commented Jan 11, 2014

Very nice. I already use Durandal for views and viewmodels, but adding it to KO would be nice. As well as the syntax for binding without the data-bind.

One thing that you may or might not be able to solve, but I struggle with, is routing with cascading form fields. My code always comes out ugly. So if I have a countries and states drop down and i want my url to be /mypage/usa/ny - and have that url load the proper settings in the drop down when the user goes to it. Its never pretty.

As I said, I am not sure of these updates will help with that and of course you don't want to add routing to KO, but maybe somehow the components can be routing aware.

@JeffSpies

This comment has been minimized.

Show comment
Hide comment
@JeffSpies

JeffSpies Jan 11, 2014

This is great--components are exactly what we're looking for, sort of have put together in a less than elegant way (lots of inefficient reloading of view models), and what I'm actively putting together as we speak. I'd like to make the suggestion that the template engine be left abstracted (i.e. interchangeable) along with the text interpolation syntax.

My broader use-case includes history.js, require.js, and knockout--perhaps, I'll see if I can make cross-roads play nice with history.js and your prototype.

This is great--components are exactly what we're looking for, sort of have put together in a less than elegant way (lots of inefficient reloading of view models), and what I'm actively putting together as we speak. I'd like to make the suggestion that the template engine be left abstracted (i.e. interchangeable) along with the text interpolation syntax.

My broader use-case includes history.js, require.js, and knockout--perhaps, I'll see if I can make cross-roads play nice with history.js and your prototype.

@AnthonyPAlicea

This comment has been minimized.

Show comment
Hide comment
@AnthonyPAlicea

AnthonyPAlicea Jan 11, 2014

This is a fantastic idea and ultimately what many of us have built our own plumbing to do in various projects. Also opens a much better way to share KnockoutJS components for reuse --- a better way to wrap other JS modules, such as UI frameworks like Bootstrap and Kendo.

This is a fantastic idea and ultimately what many of us have built our own plumbing to do in various projects. Also opens a much better way to share KnockoutJS components for reuse --- a better way to wrap other JS modules, such as UI frameworks like Bootstrap and Kendo.

@thunklife

This comment has been minimized.

Show comment
Hide comment
@thunklife

thunklife Jan 11, 2014

Absolutely I see the value in components, I just haven't come around to the idea of templates AND components since the uses cases seem so similar to me; though components do seem more powerful.

Is the end-game to eventually phase out the template binding, leaving only components?

Absolutely I see the value in components, I just haven't come around to the idea of templates AND components since the uses cases seem so similar to me; though components do seem more powerful.

Is the end-game to eventually phase out the template binding, leaving only components?

@brigand

This comment has been minimized.

Show comment
Hide comment
@brigand

brigand Jan 11, 2014

@SteveSanderson This is great, and as it's been pointed out; this stuff has had to be hacked previously (observable for the name pointing to an empty template; and then async load the template into the DOM; and change the name observable to match its ID, etc.);

@wilhelmson templates will still be useful, especially for recursive things like nested comments, however you may use this inside a component.

Speaking of templates in components; there should be a way to have templates without using IDs. IDs are unique in a page; which would mean a component would have to make assumptions about the page it's in; and you can't use a component with an ID based template more than once on a page.

The {{ }} like syntax is great. A lot of template engines will convert it into JavaScript, which can then be executed, often wrapping it in a function.

foo {{ bar }} baz

// becomes
"foo " + ko.unwrap(bar) + " baz"

Then you can wrap this in a computed which is connected to the attribute/textContent of the element.

brigand commented Jan 11, 2014

@SteveSanderson This is great, and as it's been pointed out; this stuff has had to be hacked previously (observable for the name pointing to an empty template; and then async load the template into the DOM; and change the name observable to match its ID, etc.);

@wilhelmson templates will still be useful, especially for recursive things like nested comments, however you may use this inside a component.

Speaking of templates in components; there should be a way to have templates without using IDs. IDs are unique in a page; which would mean a component would have to make assumptions about the page it's in; and you can't use a component with an ID based template more than once on a page.

The {{ }} like syntax is great. A lot of template engines will convert it into JavaScript, which can then be executed, often wrapping it in a function.

foo {{ bar }} baz

// becomes
"foo " + ko.unwrap(bar) + " baz"

Then you can wrap this in a computed which is connected to the attribute/textContent of the element.

@garvincasimir

This comment has been minimized.

Show comment
Hide comment
@garvincasimir

garvincasimir Jan 11, 2014

I like the text interpolation feature. This will certainly allow for some cleaner markup. I understand your point about components and could see where they could be useful but I just don't like the custom elements thing in ko or angular...just me.

I like the text interpolation feature. This will certainly allow for some cleaner markup. I understand your point about components and could see where they could be useful but I just don't like the custom elements thing in ko or angular...just me.

@brettwgreen

This comment has been minimized.

Show comment
Hide comment
@brettwgreen

brettwgreen Jan 11, 2014

We could absolutely use components in our code... It would be very helpful. And I have found the generic attr binding to be a bit annoying. Simple string inserting would be a useful addition, although I could see it being greatly overused.

We could absolutely use components in our code... It would be very helpful. And I have found the generic attr binding to be a bit annoying. Simple string inserting would be a useful addition, although I could see it being greatly overused.

@milutinovici

This comment has been minimized.

Show comment
Hide comment
@milutinovici

milutinovici Jan 11, 2014

I like the proposed changes. But I think that we do not need component binding. I would prefer extending template binding, and if you wanted to consume it using custom elements, you would have to register it as a component.

I like the proposed changes. But I think that we do not need component binding. I would prefer extending template binding, and if you wanted to consume it using custom elements, you would have to register it as a component.

@mariusGundersen

This comment has been minimized.

Show comment
Hide comment
@mariusGundersen

mariusGundersen Jan 11, 2014

Contributor

I like this!

I feel the way components are registered should be thought thoroughly through. The way bindings (and extenders) are registered means they have to be preloaded at startup. It would be great if binding lookup (from ko.bindinghandlers) had a callback, so the custom binding could be loaded with AMD if it isn't already loaded. The same should apply to components. If it comes across a component in the DOM which hasn't been registered, it should support a hook with a callback/promise so the component can be lazy loaded with AMD.

Contributor

mariusGundersen commented Jan 11, 2014

I like this!

I feel the way components are registered should be thought thoroughly through. The way bindings (and extenders) are registered means they have to be preloaded at startup. It would be great if binding lookup (from ko.bindinghandlers) had a callback, so the custom binding could be loaded with AMD if it isn't already loaded. The same should apply to components. If it comes across a component in the DOM which hasn't been registered, it should support a hook with a callback/promise so the component can be lazy loaded with AMD.

@johnnyreilly

This comment has been minimized.

Show comment
Hide comment
@johnnyreilly

johnnyreilly Jan 11, 2014

Very exciting - I like it all!

Very exciting - I like it all!

@chitza

This comment has been minimized.

Show comment
Hide comment
@chitza

chitza Jan 11, 2014

I would really love to see the components feature implemented. We're building a framework based on knockout and this would help us tremendously since we have already developed our own custom view models for complex UI elements such as grids.

chitza commented Jan 11, 2014

I would really love to see the components feature implemented. We're building a framework based on knockout and this would help us tremendously since we have already developed our own custom view models for complex UI elements such as grids.

@mbest

This comment has been minimized.

Show comment
Hide comment
@mbest

mbest Jan 11, 2014

Member

Thanks, Steve, for going into detail about your ideas. I've used the component pattern myself while developing Knockout applications, so I think it would great to have a built-in mechanism for this. I'd also like to see it support composition and inheritance.

Regarding text interpolation in attributes, I've seen how Angular has had to jump through hoops and create lots of custom attributes to support this. Possibly, we could support it for a limited set of attributes (like title, name, id) or maybe blacklist ones that are known to cause trouble (like src, disabled, class).

I'm not sure I like the custom elements idea. I'll have to think about that one.

Member

mbest commented Jan 11, 2014

Thanks, Steve, for going into detail about your ideas. I've used the component pattern myself while developing Knockout applications, so I think it would great to have a built-in mechanism for this. I'd also like to see it support composition and inheritance.

Regarding text interpolation in attributes, I've seen how Angular has had to jump through hoops and create lots of custom attributes to support this. Possibly, we could support it for a limited set of attributes (like title, name, id) or maybe blacklist ones that are known to cause trouble (like src, disabled, class).

I'm not sure I like the custom elements idea. I'll have to think about that one.

@mbest

This comment has been minimized.

Show comment
Hide comment
@mbest

mbest Jan 11, 2014

Member

It would be great if binding lookup (from ko.bindinghandlers) had a callback

@mariusGundersen, this can already be done. Knockout loads all bindings using the ko.getBindingHandler function, which you can overload to add whatever functionality you like. Check out Knockout.Punches to see an example of this.

Member

mbest commented Jan 11, 2014

It would be great if binding lookup (from ko.bindinghandlers) had a callback

@mariusGundersen, this can already be done. Knockout loads all bindings using the ko.getBindingHandler function, which you can overload to add whatever functionality you like. Check out Knockout.Punches to see an example of this.

@craigktreasure

This comment has been minimized.

Show comment
Hide comment
@craigktreasure

craigktreasure Jan 11, 2014

I like the components and custom elements. Cleans up the markup a lot and simplifies plugins. I think the text interpolation is cool, but should be left as a plugin if its not performant.

I like the components and custom elements. Cleans up the markup a lot and simplifies plugins. I think the text interpolation is cool, but should be left as a plugin if its not performant.

@gaydenko

This comment has been minimized.

Show comment
Hide comment
@gaydenko

gaydenko Jan 11, 2014

Being very new on the client side (be prepare to crazy reading) I have started from learning AngularJS (and JS itself :)) and have wrote complete (not too big) application. And that experience was a motivation to leave that framework and wrote own (tiny) one based on Knockout (and a couple another very small lib for routing and validation). To my taste custom elements (as well as attributes) just look attractive, but on real development process Knockout single-point data-bind (with comma-separated bindings) approach results in much clear markup and overall development process (read thinking).

As for components, well, it is rather strange word. I look at components as at application parts (with life cycle, children and autocleaning them with (if supplied) destroy() and autounsubscribing, pubsub with named buses and bus inheritance, sandboxes and so on - starting from Zakas :)) are not obligatory related to V (and V-VM). Some of then can be visual (have V, or be related to DOM subtree), and some of last ones can have VM.

So, I'm writing all these words with simple idea: there are (at least, valuable at current context) two spaces of components: components as application modules and DOM related components (with VM). And, accordingly, there are two composition mechanics (@mbest has mentioned it) - for application modules and for KO components. And would be great to try to keep these spaces orthogonal :)

Being very new on the client side (be prepare to crazy reading) I have started from learning AngularJS (and JS itself :)) and have wrote complete (not too big) application. And that experience was a motivation to leave that framework and wrote own (tiny) one based on Knockout (and a couple another very small lib for routing and validation). To my taste custom elements (as well as attributes) just look attractive, but on real development process Knockout single-point data-bind (with comma-separated bindings) approach results in much clear markup and overall development process (read thinking).

As for components, well, it is rather strange word. I look at components as at application parts (with life cycle, children and autocleaning them with (if supplied) destroy() and autounsubscribing, pubsub with named buses and bus inheritance, sandboxes and so on - starting from Zakas :)) are not obligatory related to V (and V-VM). Some of then can be visual (have V, or be related to DOM subtree), and some of last ones can have VM.

So, I'm writing all these words with simple idea: there are (at least, valuable at current context) two spaces of components: components as application modules and DOM related components (with VM). And, accordingly, there are two composition mechanics (@mbest has mentioned it) - for application modules and for KO components. And would be great to try to keep these spaces orthogonal :)

@PrivateJson

This comment has been minimized.

Show comment
Hide comment
@PrivateJson

PrivateJson Jan 11, 2014

Text Interpolation - great, we need this. I hate having to put in span's all over the place, or put the text parts in JS to handle it as it is today!
Components - great again! Unless I'm missing the point, I've been doing this with R Niemeyer's library, Knockout AMD helpers (using RequireJS etc.) but feel it should be part of the core. It will certainly make it easier than it is today!

Text Interpolation - great, we need this. I hate having to put in span's all over the place, or put the text parts in JS to handle it as it is today!
Components - great again! Unless I'm missing the point, I've been doing this with R Niemeyer's library, Knockout AMD helpers (using RequireJS etc.) but feel it should be part of the core. It will certainly make it easier than it is today!

@stalniy

This comment has been minimized.

Show comment
Hide comment
@stalniy

stalniy Jan 11, 2014

It nice to hear that you want to make things re-usable. During the last 5 months I have been working on 3 SPA which are created based on my architecture. So, I want to share my experience maybe you will find some ideas interesting.

Few months ago I started to think about re-usable components in ko and ended up with 3 things which are almost always present in MVVM based applications:

Router

Router is just a url to controller mapper

router
  .when('/user/:id', { controller: 'users.item' })
  .otherwise({ controller: 'dashboard' });

Controler

More like view model builder and updater.

var Controller = {
  defineScope: function (requestParams) {
     // define our view model
     return new User();
  },

 dispatch: function (requestParams, user) {
    // update our view model when url is updated
    if (requestParams.id) {
      user.load(requestParams.id);
    }
 }
};

View model

It is a well known thing (i.e. scope).

So, then I needed a solution how to make them work together. And I ended up with 3 more abstractions.

Binder

Binder is responsible for binding controllers on domNodes. Interface can be like this:

  • bindController(params, rootNode); // params = { controller: "users.item", template: "users.edit" };
  • execControllerOn(rootNode, controllerObject, params);

Renderer

We can re-use native template system which is quite powerful.

Entity loader

Object which is responsible for resolving template/scope/controller names to objects:

  • loadEntity(name, type); // type can be scope, controller, template

Then we can easily create data-bindings controller and scope. Their simplified versions:

//scope binding
ko.bindingHandlers.scope = {
    init: function (element, valueAccessor, allBindingsAccessor, currentScope, currentContext) {
      var options = allBindingsAccessor();
      var scopeName   = valueAccessor();

      loader.loadEntity(scopeName, 'scope').done(function (viewModel) {
        var controller = createBareControllerFor(viewModel, options, currentContext);

        binder.execControllerOn(element, controller, {
          templateName : allBindings.templateName || null,
          controller   : scopeName
        });
      });

      return { controlsDescendantBindings: true };
    }
};

ko.bindingHandlers.controller = {
  init: function (element, valueAccessor, allBindingsAccessor) {
    var allBindings = allBindingsAccessor();

    binder.bindController({
      templateName : allBindings.templateName || null,
      controller   : valueAccessor()
    }, element);

    return { controlsDescendantBindings: true };
  }
};

So, at this point we have reusable and independent components in the form of controller or scope. And now it's quite easy to bind them inside html:

<div data-bind="scope: 'users.item', templateName: 'users.item'"></div>

<!-- OR -->

<div data-bind="controller: 'users.item', templateName: 'users.item'"></div>

Then using preprocessNode method in knockout3.0 it's possible to create custom elements like:

<scope name="users.item" template="users.item" />

<!-- OR --> 

<controller name="users.item" />

After all I have 3 types of reusable components:

  • data-binding (usually are used for wrapping third party plugins) <- the lowest level
  • scope (can be any kind of view model which can be applied to different templates)
  • controller (usually is used for complex view models which depends on url or have complex dependencies) <- the highest level

Typical controller for complex view model:

define([ ..... ], function (Grid, schema, RestAdapter) {
  return {
    defineScope: function () {
      var adapter = new RestAdapter(schema.devices);
      var grid = new Grid(adapter);

      return grid.load();
    }
  };
});

stalniy commented Jan 11, 2014

It nice to hear that you want to make things re-usable. During the last 5 months I have been working on 3 SPA which are created based on my architecture. So, I want to share my experience maybe you will find some ideas interesting.

Few months ago I started to think about re-usable components in ko and ended up with 3 things which are almost always present in MVVM based applications:

Router

Router is just a url to controller mapper

router
  .when('/user/:id', { controller: 'users.item' })
  .otherwise({ controller: 'dashboard' });

Controler

More like view model builder and updater.

var Controller = {
  defineScope: function (requestParams) {
     // define our view model
     return new User();
  },

 dispatch: function (requestParams, user) {
    // update our view model when url is updated
    if (requestParams.id) {
      user.load(requestParams.id);
    }
 }
};

View model

It is a well known thing (i.e. scope).

So, then I needed a solution how to make them work together. And I ended up with 3 more abstractions.

Binder

Binder is responsible for binding controllers on domNodes. Interface can be like this:

  • bindController(params, rootNode); // params = { controller: "users.item", template: "users.edit" };
  • execControllerOn(rootNode, controllerObject, params);

Renderer

We can re-use native template system which is quite powerful.

Entity loader

Object which is responsible for resolving template/scope/controller names to objects:

  • loadEntity(name, type); // type can be scope, controller, template

Then we can easily create data-bindings controller and scope. Their simplified versions:

//scope binding
ko.bindingHandlers.scope = {
    init: function (element, valueAccessor, allBindingsAccessor, currentScope, currentContext) {
      var options = allBindingsAccessor();
      var scopeName   = valueAccessor();

      loader.loadEntity(scopeName, 'scope').done(function (viewModel) {
        var controller = createBareControllerFor(viewModel, options, currentContext);

        binder.execControllerOn(element, controller, {
          templateName : allBindings.templateName || null,
          controller   : scopeName
        });
      });

      return { controlsDescendantBindings: true };
    }
};

ko.bindingHandlers.controller = {
  init: function (element, valueAccessor, allBindingsAccessor) {
    var allBindings = allBindingsAccessor();

    binder.bindController({
      templateName : allBindings.templateName || null,
      controller   : valueAccessor()
    }, element);

    return { controlsDescendantBindings: true };
  }
};

So, at this point we have reusable and independent components in the form of controller or scope. And now it's quite easy to bind them inside html:

<div data-bind="scope: 'users.item', templateName: 'users.item'"></div>

<!-- OR -->

<div data-bind="controller: 'users.item', templateName: 'users.item'"></div>

Then using preprocessNode method in knockout3.0 it's possible to create custom elements like:

<scope name="users.item" template="users.item" />

<!-- OR --> 

<controller name="users.item" />

After all I have 3 types of reusable components:

  • data-binding (usually are used for wrapping third party plugins) <- the lowest level
  • scope (can be any kind of view model which can be applied to different templates)
  • controller (usually is used for complex view models which depends on url or have complex dependencies) <- the highest level

Typical controller for complex view model:

define([ ..... ], function (Grid, schema, RestAdapter) {
  return {
    defineScope: function () {
      var adapter = new RestAdapter(schema.devices);
      var grid = new Grid(adapter);

      return grid.load();
    }
  };
});
@chester89

This comment has been minimized.

Show comment
Hide comment
@chester89

chester89 Jan 11, 2014

Text interpolation would be nice to have. About components - I think developers dealing with UI libraries like jQuery.UI would absolutely love that 👍

Text interpolation would be nice to have. About components - I think developers dealing with UI libraries like jQuery.UI would absolutely love that 👍

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 11, 2014

Great Move!
Moving all these features inside the knockout.js core, will trigger a whole echosystem of knockout based frameworks! The right move to promote the use of knockout based SPA frameworks.

I would like to add just two criticisms:

  1. The double curl notation {{...}} is a very nice shortcut, so introducing it in the knockout.js core is a good move. However, I hope that this choice will never lead to the removal of the data-bind attribute that is much more powerful than the simpler {{...}}.

  2. I think that the automatic loading of component parts by providing an url should not be inserted in the knockout.js core, since it is more a framework related feature. In fact, the way dynamic loading is performed may depend on other features of the framework based on knockout.js, and inserting a standard way to do it, mightt be interpreted as a kind of standard to be followed by all frameworks. This, "de facto" standard might put too much constraints on the frameworks implemented with knocout.js.
    In fact components dynamci loading, involves stuffs like caching, and the way viewmodels and templates are organized on the server side, that are tightly tied with the way a framework works.

Great Move!
Moving all these features inside the knockout.js core, will trigger a whole echosystem of knockout based frameworks! The right move to promote the use of knockout based SPA frameworks.

I would like to add just two criticisms:

  1. The double curl notation {{...}} is a very nice shortcut, so introducing it in the knockout.js core is a good move. However, I hope that this choice will never lead to the removal of the data-bind attribute that is much more powerful than the simpler {{...}}.

  2. I think that the automatic loading of component parts by providing an url should not be inserted in the knockout.js core, since it is more a framework related feature. In fact, the way dynamic loading is performed may depend on other features of the framework based on knockout.js, and inserting a standard way to do it, mightt be interpreted as a kind of standard to be followed by all frameworks. This, "de facto" standard might put too much constraints on the frameworks implemented with knocout.js.
    In fact components dynamci loading, involves stuffs like caching, and the way viewmodels and templates are organized on the server side, that are tightly tied with the way a framework works.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Jan 11, 2014

That is absolutely excellent, I loved the 3.0.0 release and this could turn out as great if not greater!

I have a few questions and remarks:

  • What is "A method that returns an object asynchronously"? Are you thinking of Promises/thenables? I hope there is no callback involved :)
  • I really hope knockout remains a pure data-binding library. it's half of the reasons I use it :-) The integration points should remain fully open. This leads me to: How tight is the integration with AMD? Would it just be a call to require()? Please note that many people are moving from AMD to CommonJS (Often using browserify) as it's simpler, has fewer issues and is always synchronous both in dev and in prod mode. It wouldn't be nice to associate knockout too strongly with just one of the non-native module systems. Similarly, I like to use my own router (https://github.com/AlexGalays/abyssa-js) and if each library maintain absolute separation of concerns they can continue playing well together.
  • Would it be easy to make the text binding syntax configurable? I know many template libraries don't offer that but it can be a huge pain. For instance, I could have a single page app using a majority of handlebars templates for raw speed and sprinkle knockout here and there on top where it really shines. Then perhaps I would want Handlebars to leave simple curly bracket {} or @{} or #{}, etc alone for knockout bindings.
  • "Browser support right back to IE6". Slightly unrelated to the topic but, does supporting IE6 and IE7 occurs a lot of added effort for the core contributors and file size? If the overhead is big, it could be the occasion to drop the support? Many project choose IE8 as the oldest browser nowadays.
  • So we would have encapsulated components with a way to declaratively feed them dynamic data in a write-only fashion. How do we get data out? Surely we don't want something overly complicated like [Some competitor] but supporting two-way data binding (Alternative syntax working only with simple expressions?) and/or custom events would be I think, mandatory to have truly encapsulated components.

Cheers

That is absolutely excellent, I loved the 3.0.0 release and this could turn out as great if not greater!

I have a few questions and remarks:

  • What is "A method that returns an object asynchronously"? Are you thinking of Promises/thenables? I hope there is no callback involved :)
  • I really hope knockout remains a pure data-binding library. it's half of the reasons I use it :-) The integration points should remain fully open. This leads me to: How tight is the integration with AMD? Would it just be a call to require()? Please note that many people are moving from AMD to CommonJS (Often using browserify) as it's simpler, has fewer issues and is always synchronous both in dev and in prod mode. It wouldn't be nice to associate knockout too strongly with just one of the non-native module systems. Similarly, I like to use my own router (https://github.com/AlexGalays/abyssa-js) and if each library maintain absolute separation of concerns they can continue playing well together.
  • Would it be easy to make the text binding syntax configurable? I know many template libraries don't offer that but it can be a huge pain. For instance, I could have a single page app using a majority of handlebars templates for raw speed and sprinkle knockout here and there on top where it really shines. Then perhaps I would want Handlebars to leave simple curly bracket {} or @{} or #{}, etc alone for knockout bindings.
  • "Browser support right back to IE6". Slightly unrelated to the topic but, does supporting IE6 and IE7 occurs a lot of added effort for the core contributors and file size? If the overhead is big, it could be the occasion to drop the support? Many project choose IE8 as the oldest browser nowadays.
  • So we would have encapsulated components with a way to declaratively feed them dynamic data in a write-only fashion. How do we get data out? Surely we don't want something overly complicated like [Some competitor] but supporting two-way data binding (Alternative syntax working only with simple expressions?) and/or custom events would be I think, mandatory to have truly encapsulated components.

Cheers

@rniemeyer

This comment has been minimized.

Show comment
Hide comment
@rniemeyer

rniemeyer Jan 11, 2014

Member

Very interesting ideas. The text interpolation sounds like a great feature for us to explore adding and would simplify a heavily used part of KO.

For components, I am definitely interested in the specifics of how we can implement it, especially in how it relates to module systems/loaders. We have had good success with a component style using requirejs and my knockout-amd-helpers library. It makes the template engine automatically load templates via the AMD loader's text plugin and provides a module binding that dynamically combines view models and templates together (with some flexibility to specify some callbacks, pass initialization data, override the template, and/or use an anonymous template). The binding also supports various options being observable, so you can swap modules/templates dynamically. In our optimized release builds, we include the templates/view models to avoid the extra time/cost of extra HTTP requests. Durandal has similar functionality in its compose binding.

The component stuff feels a bit like a plugin to me at first glance. I would be interested though in helping figure out how it can be implemented in a generic, but useful way. In the core, seems like we would not want to tie it specifically to other libraries (module systems/loaders), but I also don't think that we want KO components to turn into our own module/loading system. With that said, it would be wonderful if devs could easily build KO apps from day 1 without having to put tons of templates in script tags and without having a giant app level view model.

Custom elements haven't personally peaked my interest a lot yet, but web development does seem to be heading that way and it is certainly a great option to provide and a nice way to interact with components.

Member

rniemeyer commented Jan 11, 2014

Very interesting ideas. The text interpolation sounds like a great feature for us to explore adding and would simplify a heavily used part of KO.

For components, I am definitely interested in the specifics of how we can implement it, especially in how it relates to module systems/loaders. We have had good success with a component style using requirejs and my knockout-amd-helpers library. It makes the template engine automatically load templates via the AMD loader's text plugin and provides a module binding that dynamically combines view models and templates together (with some flexibility to specify some callbacks, pass initialization data, override the template, and/or use an anonymous template). The binding also supports various options being observable, so you can swap modules/templates dynamically. In our optimized release builds, we include the templates/view models to avoid the extra time/cost of extra HTTP requests. Durandal has similar functionality in its compose binding.

The component stuff feels a bit like a plugin to me at first glance. I would be interested though in helping figure out how it can be implemented in a generic, but useful way. In the core, seems like we would not want to tie it specifically to other libraries (module systems/loaders), but I also don't think that we want KO components to turn into our own module/loading system. With that said, it would be wonderful if devs could easily build KO apps from day 1 without having to put tons of templates in script tags and without having a giant app level view model.

Custom elements haven't personally peaked my interest a lot yet, but web development does seem to be heading that way and it is certainly a great option to provide and a nice way to interact with components.

@mariusGundersen

This comment has been minimized.

Show comment
Hide comment
@mariusGundersen

mariusGundersen Jan 11, 2014

Contributor

Thanks @mbest, didn't know of that. I don't think AMD should be built into knockout.js, especially with ES6 modules right around the corner. As long as components (and custom bindings) are fetched with an async function, either through a callback or a promise, then people can use the loading method they want, including bundling everything together and using AMD or ES6 modules to load them.

Contributor

mariusGundersen commented Jan 11, 2014

Thanks @mbest, didn't know of that. I don't think AMD should be built into knockout.js, especially with ES6 modules right around the corner. As long as components (and custom bindings) are fetched with an async function, either through a callback or a promise, then people can use the loading method they want, including bundling everything together and using AMD or ES6 modules to load them.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 11, 2014

@rniemeyer,
the component stuff may be included in the core, but just as a way of glueing together template with its viewmodel. It is in the spirit of Web Components. However, automatic loading should not be included in the core, since several possible oprtions are possible and each framework should be free to organize its loading strategy.There is no "better way" to do it.

@rniemeyer,
the component stuff may be included in the core, but just as a way of glueing together template with its viewmodel. It is in the spirit of Web Components. However, automatic loading should not be included in the core, since several possible oprtions are possible and each framework should be free to organize its loading strategy.There is no "better way" to do it.

@dylanized

This comment has been minimized.

Show comment
Hide comment
@dylanized

dylanized Jan 11, 2014

This looks so awesome. yes yes yes :) especially for the Interpolated Text, that is huge. (I think Ember is adding this in the new version, powered by https://github.com/tildeio/htmlbars).

Regarding what's "core", and what's not. What if Knockout was divided into a core (a minimal version, like now), and a few "official" plugins: perhaps KO Mapping, KO Components, KO Elements, and KO Tags. The default build could be aimed for the most newb-friendly option (possibly Core + Mapping + Tags? or just Core + Tags?), but experienced users could configure their own build that contains more or less.

(Twitter Bootstrap sorta takes this approach - the default build is feature rich, but it's easy to grab just the reset and grid.)

But yeah - this totally rocks!!

This looks so awesome. yes yes yes :) especially for the Interpolated Text, that is huge. (I think Ember is adding this in the new version, powered by https://github.com/tildeio/htmlbars).

Regarding what's "core", and what's not. What if Knockout was divided into a core (a minimal version, like now), and a few "official" plugins: perhaps KO Mapping, KO Components, KO Elements, and KO Tags. The default build could be aimed for the most newb-friendly option (possibly Core + Mapping + Tags? or just Core + Tags?), but experienced users could configure their own build that contains more or less.

(Twitter Bootstrap sorta takes this approach - the default build is feature rich, but it's easy to grab just the reset and grid.)

But yeah - this totally rocks!!

@rniemeyer

This comment has been minimized.

Show comment
Hide comment
@rniemeyer

rniemeyer Jan 11, 2014

Member

@frankabbruzzese - right. The template binding already handles dynamic template name/data, so I am mostly interested in how we structure components on top of existing functionality without coupling it to other libraries, but still making it useful out-of-the-box. If it means providing extensibility in KO and still requiring a plugin, then I want to see how this can improve on the existing options for this style of development (things like Durandal and amd-helpers).

Member

rniemeyer commented Jan 11, 2014

@frankabbruzzese - right. The template binding already handles dynamic template name/data, so I am mostly interested in how we structure components on top of existing functionality without coupling it to other libraries, but still making it useful out-of-the-box. If it means providing extensibility in KO and still requiring a plugin, then I want to see how this can improve on the existing options for this style of development (things like Durandal and amd-helpers).

@thunklife

This comment has been minimized.

Show comment
Hide comment
@thunklife

thunklife Jan 11, 2014

I second @rniemeyer and @frankabbruzzese. If I think of components as just a common way to glue templates and models together, then it makes sense to have it in core. What I would hate to see is KO impose requirements on project structure or dependencies. I feel like that moves things out of the library space (which I personally prefer), and into the framework space.

I second @rniemeyer and @frankabbruzzese. If I think of components as just a common way to glue templates and models together, then it makes sense to have it in core. What I would hate to see is KO impose requirements on project structure or dependencies. I feel like that moves things out of the library space (which I personally prefer), and into the framework space.

@timstuyckens

This comment has been minimized.

Show comment
Hide comment
@timstuyckens

timstuyckens Jan 11, 2014

Definitly the way to go!
knockoutjs should provide a build-in way to encapsulate a template and corresponding viewmodel.
Allowing the viewmodel to also be a plain object (instead of just a AMD module) will lower the barrier.

The views would be a lot cleaner if we can use the proposed syntax for custom elements. Right now I see a lot of

<div data-bind="someCustomBinding :optionsObject /> 

where the someCustomBinding adds dom to the div and attaches event handlers. eg: datepicker, select2 bindings or bootstrap components like http://billpull.github.io/knockout-bootstrap/
components and custom elements would solve this.

For the text interpolation. I know http://mbest.github.io/knockout.punches/ provides this but putting it into the core would be great. New knockoutjs developers often just look to the examples on http://knockoutjs.com/ and only use what is in the core.

Definitly the way to go!
knockoutjs should provide a build-in way to encapsulate a template and corresponding viewmodel.
Allowing the viewmodel to also be a plain object (instead of just a AMD module) will lower the barrier.

The views would be a lot cleaner if we can use the proposed syntax for custom elements. Right now I see a lot of

<div data-bind="someCustomBinding :optionsObject /> 

where the someCustomBinding adds dom to the div and attaches event handlers. eg: datepicker, select2 bindings or bootstrap components like http://billpull.github.io/knockout-bootstrap/
components and custom elements would solve this.

For the text interpolation. I know http://mbest.github.io/knockout.punches/ provides this but putting it into the core would be great. New knockoutjs developers often just look to the examples on http://knockoutjs.com/ and only use what is in the core.

@thelinuxlich

This comment has been minimized.

Show comment
Hide comment

+10000

@EisenbergEffect

This comment has been minimized.

Show comment
Hide comment
@EisenbergEffect

EisenbergEffect Jan 11, 2014

Pretty serious overlap with Durandal on the components feature.

I believe all of this, except the custom elements, can be done today with Durandal and Knockout.Punches. I was planning on building a custom element api using the new 3.0 hooks, but if you've already got it, I'm happy to use that.

Personally, my feelings are pretty mixed on this, because while it's nice to have the text interpolation and custom elements...if you add components too...it's going to create a serious headache for me (massive re-writes). One of the reasons Durandal uses Knockout is because Knockout has been fairly well-focused in the past. I believe adding components transitions it out of a simple "library" for databinding and more into SPA framework territory. The only think you are missing at that point is a router.

So, my vote is probably to add only two things:

  1. Text interpolation
  2. An api for registering custom elements (with lifecycle callbacks please), but not any pre-conceived notion of how an element should be implemented, such as component functionality.

Pretty serious overlap with Durandal on the components feature.

I believe all of this, except the custom elements, can be done today with Durandal and Knockout.Punches. I was planning on building a custom element api using the new 3.0 hooks, but if you've already got it, I'm happy to use that.

Personally, my feelings are pretty mixed on this, because while it's nice to have the text interpolation and custom elements...if you add components too...it's going to create a serious headache for me (massive re-writes). One of the reasons Durandal uses Knockout is because Knockout has been fairly well-focused in the past. I believe adding components transitions it out of a simple "library" for databinding and more into SPA framework territory. The only think you are missing at that point is a router.

So, my vote is probably to add only two things:

  1. Text interpolation
  2. An api for registering custom elements (with lifecycle callbacks please), but not any pre-conceived notion of how an element should be implemented, such as component functionality.
@PWKad

This comment has been minimized.

Show comment
Hide comment

PWKad commented Jan 11, 2014

True

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jan 11, 2014

Contributor

Thanks so much for all the feedback! My sense is that this strongly validates that there's a need here. There are a lot of points about keeping things open, flexible, unopinionated, and not tied to specific external technologies and requirements. Well, that all sounds right to me, and is exactly what we usually aim for :)

Addressing some specific questions:

@WillHelmson

Is the end-game to eventually phase out the template binding, leaving only components?

Absolutely not - templates are the basis for many important things that people do with Knockout, including the built-in control flow bindings. There's no reason why we'd want to phase that out.

@mariusGundersen

I feel the way components are registered should be thought thoroughly through.

Of course :) Hopefully we can find the simplest possible thing for the base level, and leave it open for extension (e.g., via a callback, like you mention).

@mbest

Regarding text interpolation in attributes [...] we could support it for a limited set of attributes

That's interesting. Maybe that is the right way to achieve the desired result without compromising performance. We could preconfigure it to work with basically all the element/attribute pairs that we think are realistically useful, and leave it open for developers to add additional element/attribute pairs if they want.

I'd also like to see it support composition and inheritance.

Great points. Let's be sure to account for that. I'm hopeful that as long as we keep the base level as primitive as possible - for example just letting developers asynchronously return constructor functions for components - then it will be possible to use a range of JavaScript techniques to achieve whatever kinds of inheritance/composition are required. We can definitely make it more convenient than inheriting/composing binding handlers :)

@gaydenko

As for components, well, it is rather strange word. I look at components as at application parts (with life cycle, children and autocleaning them with (if supplied) destroy() and autounsubscribing, pubsub with named buses and bus inheritance, sandboxes and so on

That's interesting. I suspect your use of the word "components" is quite different to what I'm proposing here. All I mean by "components" are things that have both views and viewmodels, and can be included asynchronously. This is very general-purpose and more specialised things (such as pub/sub) can be built on top of it in your application if desired.

@frankabbruzzese

The double curl notation {{...}} is a very nice shortcut, so introducing it in the knockout.js core is a good move. However, I hope that this choice will never lead to the removal of the data-bind attribute

Don't worry - nobody is proposing to remove data-bind! I think everybody here would agree that is a powerful and effective way to achieve bindings in general, and will remain at the core of how KO deals with bindings.

@AlexGalays

What is "A method that returns an object asynchronously"? Are you thinking of Promises/thenables? I hope there is no callback involved :)

In keeping with KO's goal to remain as general-purpose and interoperable as possible, I do mean a callback, because that's the least opinionated thing you can do in JavaScript. People who want to use a particular flavour of promises/thenables can easily work with callbacks.

For example, here's how some config might look if KO was going to have native understanding of thenables:

getSomething: function(params) { return createPromise(params); }

... whereas here's how it would look if KO was to remain agnostic to any particular notion of a promise:

getSomething: function(params, callback) { return createPromise(params).then(callback); }

It's a tiny bit longer, but no harder to work with really. And if you are keen on standardising promises in your app, you can easily make a function that wraps ko.components.register and calls callback when your promise completes. Best of all worlds.

How tight is the integration with AMD? Would it just be a call to require()?

I would anticipate that, at the very most, we'd just support something like:

viewModel: { require: "myModule" }

... and handle it by calling window.require("myModule", callback). This means we have no real tie to require.js or CommonJS or Browserify or anything else that specifically addresses the actual loading tasks. It would just streamline the use of any of these require()-based technologies.

And of course, this would be built on a lower-level still whereby you just call a callback to supply your object, and then you can integrate with any imaginable loader.

Would it be easy to make the text binding syntax configurable?

We probably won't know until we try implementing it.

does supporting IE6 and IE7 occurs a lot of added effort for the core contributors and file size? ... Many project choose IE8 as the oldest browser nowadays.

It only took me a couple of hours to track down and fix all the old-IE issues in the prototype, so I don't think it's bad. In any case, supporting IE 6 is pretty much no extra work if you are going to support IE 8, since IE didn't actually enter the modern age until IE 9.

we would have encapsulated components with a way to declaratively feed them dynamic data in a write-only fashion. How do we get data out?

There are two convenient ways it works in the prototype: (1) if you are passing observables, then the component receiving them can write back to them if you want. (2) if the custom element syntax notices your expression involves dependencies (and hence can change), we can wrap its accessors in a writable computed, so that the invoked component can write back to it. This is useful if you're building something like a custom edit control, and want to be able to write <my-editor value="{{ something }}"></my-editor>.

@rniemeyer

I am definitely interested in the specifics of how we can implement it, especially in how it relates to module systems/loaders. We have had good success with a component style using requirejs and my knockout-amd-helpers library

It's great you've got that experience, and no doubt it will come in extremely valuable when solidifying this design. My expectation was that the KO core would remain completely agnostic to any particular module system, with the config being based on "just give us a callback". The one possible syntax-sugar piece might be a convenient configuration param for saying a component viewmodel can be loaded via a call to require(), since it would be totally trivial (probably one line of code in KO core), and AMD is a really well-established pattern and would be useful to people using a whole range of different AMD-based loaders.

I also don't think that we want KO components to turn into our own module/loading system

I totally agree. I think KO core should just expect people to supply viewmodels/template-contents asynchronously but without being opinionated about how you actually get the data. People who want to use an AMD loader can do so; people who want to use $.ajax can do so, and people who just want to return objects they already have in memory (all loaded up front) can do so.

Custom elements haven't personally peaked my interest a lot yet, but web development does seem to be heading that way and it is certainly a great option to provide and a nice way to interact with components.

Yes, that's exactly the thing. KO has a great opportunity to capture the interest of web developers looking at the emerging patterns of web development and show we can do that today, and seamlessly support even legacy browsers. I think that's a compelling proposition.

@mariusGundersen, @frankabbruzzese, @wilhelmson

Yes - I think we all agree that KO should not bundle any forced dependency on specific AMD loaders, or even the AMD pattern itself. We can be more low-level than that, while also being totally effortless if you do want to use AMD.

What I would hate to see is KO impose requirements on project structure or dependencies.

There is no prospect of KO forcing any particular project structure or external dependencies on you :)

@EisenbergEffect

Pretty serious overlap with Durandal on the components feature.

I know Durandal has a components notion, but I really don't think KO core itself is in any way playing in the same space as Durandal. KO core has no aspirations (at least from me, or anyone else who's spoken here) to become a full-stack framework that provides project layout, data management, etc. The basic notion of combining a view and viewmodel into a reusable piece is extremely general and widely desired, so I do think it's totally appropriate to open that up to developers using any flavour of KO-based development. We would not be stipulating anything about how components are loaded, what kinds of lifecycle they go through, how they get data, or really anything beyond the ability to register and invoke them, so I think that's completely within the remit of a focused library.

Twitter

There was a bunch of feedback on Twitter too, so I'll share it. Here's the full list of those that expressed opinions:

@pixel9 - @stevensanderson text interpolation would help me clean up my views quite a bit!
@andiih - components would solve one of my current headaches: users pick multiple widgets/modules on 1 screen.
@rubelux - I think knockout would improve dramatically with component
@andersb79 - looks perfect for my application.
@ollicle - Excellent! Asynchronous loading components would squarely address a pain point for me right now.
@matijagrcic - #Knockout developers I'd love your feedback on this feature proposal #1273 … As always amazing work!
@fatal_2 - looks awesome, really looking forward to it. And I trust in the direction you’re taking the project
@habaneroofdoom - we would love these features for sure!
@AnthonyPAlicea - Fantastic. What we're really trying to do on large KnockoutJS projects in the first place.
@KimTranjan - AWESOME! :)
@ryexley - yes please!! Would LOVE to see all of this happen! cc: @RPNiemeyer
@gilesbradshaw - I do something a little similar with observable templates that can be pushed to the browser by SignalR

Contributor

SteveSanderson commented Jan 11, 2014

Thanks so much for all the feedback! My sense is that this strongly validates that there's a need here. There are a lot of points about keeping things open, flexible, unopinionated, and not tied to specific external technologies and requirements. Well, that all sounds right to me, and is exactly what we usually aim for :)

Addressing some specific questions:

@WillHelmson

Is the end-game to eventually phase out the template binding, leaving only components?

Absolutely not - templates are the basis for many important things that people do with Knockout, including the built-in control flow bindings. There's no reason why we'd want to phase that out.

@mariusGundersen

I feel the way components are registered should be thought thoroughly through.

Of course :) Hopefully we can find the simplest possible thing for the base level, and leave it open for extension (e.g., via a callback, like you mention).

@mbest

Regarding text interpolation in attributes [...] we could support it for a limited set of attributes

That's interesting. Maybe that is the right way to achieve the desired result without compromising performance. We could preconfigure it to work with basically all the element/attribute pairs that we think are realistically useful, and leave it open for developers to add additional element/attribute pairs if they want.

I'd also like to see it support composition and inheritance.

Great points. Let's be sure to account for that. I'm hopeful that as long as we keep the base level as primitive as possible - for example just letting developers asynchronously return constructor functions for components - then it will be possible to use a range of JavaScript techniques to achieve whatever kinds of inheritance/composition are required. We can definitely make it more convenient than inheriting/composing binding handlers :)

@gaydenko

As for components, well, it is rather strange word. I look at components as at application parts (with life cycle, children and autocleaning them with (if supplied) destroy() and autounsubscribing, pubsub with named buses and bus inheritance, sandboxes and so on

That's interesting. I suspect your use of the word "components" is quite different to what I'm proposing here. All I mean by "components" are things that have both views and viewmodels, and can be included asynchronously. This is very general-purpose and more specialised things (such as pub/sub) can be built on top of it in your application if desired.

@frankabbruzzese

The double curl notation {{...}} is a very nice shortcut, so introducing it in the knockout.js core is a good move. However, I hope that this choice will never lead to the removal of the data-bind attribute

Don't worry - nobody is proposing to remove data-bind! I think everybody here would agree that is a powerful and effective way to achieve bindings in general, and will remain at the core of how KO deals with bindings.

@AlexGalays

What is "A method that returns an object asynchronously"? Are you thinking of Promises/thenables? I hope there is no callback involved :)

In keeping with KO's goal to remain as general-purpose and interoperable as possible, I do mean a callback, because that's the least opinionated thing you can do in JavaScript. People who want to use a particular flavour of promises/thenables can easily work with callbacks.

For example, here's how some config might look if KO was going to have native understanding of thenables:

getSomething: function(params) { return createPromise(params); }

... whereas here's how it would look if KO was to remain agnostic to any particular notion of a promise:

getSomething: function(params, callback) { return createPromise(params).then(callback); }

It's a tiny bit longer, but no harder to work with really. And if you are keen on standardising promises in your app, you can easily make a function that wraps ko.components.register and calls callback when your promise completes. Best of all worlds.

How tight is the integration with AMD? Would it just be a call to require()?

I would anticipate that, at the very most, we'd just support something like:

viewModel: { require: "myModule" }

... and handle it by calling window.require("myModule", callback). This means we have no real tie to require.js or CommonJS or Browserify or anything else that specifically addresses the actual loading tasks. It would just streamline the use of any of these require()-based technologies.

And of course, this would be built on a lower-level still whereby you just call a callback to supply your object, and then you can integrate with any imaginable loader.

Would it be easy to make the text binding syntax configurable?

We probably won't know until we try implementing it.

does supporting IE6 and IE7 occurs a lot of added effort for the core contributors and file size? ... Many project choose IE8 as the oldest browser nowadays.

It only took me a couple of hours to track down and fix all the old-IE issues in the prototype, so I don't think it's bad. In any case, supporting IE 6 is pretty much no extra work if you are going to support IE 8, since IE didn't actually enter the modern age until IE 9.

we would have encapsulated components with a way to declaratively feed them dynamic data in a write-only fashion. How do we get data out?

There are two convenient ways it works in the prototype: (1) if you are passing observables, then the component receiving them can write back to them if you want. (2) if the custom element syntax notices your expression involves dependencies (and hence can change), we can wrap its accessors in a writable computed, so that the invoked component can write back to it. This is useful if you're building something like a custom edit control, and want to be able to write <my-editor value="{{ something }}"></my-editor>.

@rniemeyer

I am definitely interested in the specifics of how we can implement it, especially in how it relates to module systems/loaders. We have had good success with a component style using requirejs and my knockout-amd-helpers library

It's great you've got that experience, and no doubt it will come in extremely valuable when solidifying this design. My expectation was that the KO core would remain completely agnostic to any particular module system, with the config being based on "just give us a callback". The one possible syntax-sugar piece might be a convenient configuration param for saying a component viewmodel can be loaded via a call to require(), since it would be totally trivial (probably one line of code in KO core), and AMD is a really well-established pattern and would be useful to people using a whole range of different AMD-based loaders.

I also don't think that we want KO components to turn into our own module/loading system

I totally agree. I think KO core should just expect people to supply viewmodels/template-contents asynchronously but without being opinionated about how you actually get the data. People who want to use an AMD loader can do so; people who want to use $.ajax can do so, and people who just want to return objects they already have in memory (all loaded up front) can do so.

Custom elements haven't personally peaked my interest a lot yet, but web development does seem to be heading that way and it is certainly a great option to provide and a nice way to interact with components.

Yes, that's exactly the thing. KO has a great opportunity to capture the interest of web developers looking at the emerging patterns of web development and show we can do that today, and seamlessly support even legacy browsers. I think that's a compelling proposition.

@mariusGundersen, @frankabbruzzese, @wilhelmson

Yes - I think we all agree that KO should not bundle any forced dependency on specific AMD loaders, or even the AMD pattern itself. We can be more low-level than that, while also being totally effortless if you do want to use AMD.

What I would hate to see is KO impose requirements on project structure or dependencies.

There is no prospect of KO forcing any particular project structure or external dependencies on you :)

@EisenbergEffect

Pretty serious overlap with Durandal on the components feature.

I know Durandal has a components notion, but I really don't think KO core itself is in any way playing in the same space as Durandal. KO core has no aspirations (at least from me, or anyone else who's spoken here) to become a full-stack framework that provides project layout, data management, etc. The basic notion of combining a view and viewmodel into a reusable piece is extremely general and widely desired, so I do think it's totally appropriate to open that up to developers using any flavour of KO-based development. We would not be stipulating anything about how components are loaded, what kinds of lifecycle they go through, how they get data, or really anything beyond the ability to register and invoke them, so I think that's completely within the remit of a focused library.

Twitter

There was a bunch of feedback on Twitter too, so I'll share it. Here's the full list of those that expressed opinions:

@pixel9 - @stevensanderson text interpolation would help me clean up my views quite a bit!
@andiih - components would solve one of my current headaches: users pick multiple widgets/modules on 1 screen.
@rubelux - I think knockout would improve dramatically with component
@andersb79 - looks perfect for my application.
@ollicle - Excellent! Asynchronous loading components would squarely address a pain point for me right now.
@matijagrcic - #Knockout developers I'd love your feedback on this feature proposal #1273 … As always amazing work!
@fatal_2 - looks awesome, really looking forward to it. And I trust in the direction you’re taking the project
@habaneroofdoom - we would love these features for sure!
@AnthonyPAlicea - Fantastic. What we're really trying to do on large KnockoutJS projects in the first place.
@KimTranjan - AWESOME! :)
@ryexley - yes please!! Would LOVE to see all of this happen! cc: @RPNiemeyer
@gilesbradshaw - I do something a little similar with observable templates that can be pushed to the browser by SignalR

@mariusGundersen

This comment has been minimized.

Show comment
Hide comment
@mariusGundersen

mariusGundersen Jan 11, 2014

Contributor

BTW @stevensanderson, promises have been standardized, both by TC39 (in the ES6 spec) and by WHATWG, so promises will be a native part of both the DOM and javascript. It might be a good idea to start using promises instead of callbacks in the near future for new (and old) features.

Contributor

mariusGundersen commented Jan 11, 2014

BTW @stevensanderson, promises have been standardized, both by TC39 (in the ES6 spec) and by WHATWG, so promises will be a native part of both the DOM and javascript. It might be a good idea to start using promises instead of callbacks in the near future for new (and old) features.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@gaydenko

This comment has been minimized.

Show comment
Hide comment
@gaydenko

gaydenko Jan 12, 2014

@SteveSanderson

That's interesting. I suspect your use of the word "components" is quite different to what I'm proposing here. All I mean by "components" are things that have both views and viewmodels, and can be included asynchronously. This is very general-purpose and more specialised things (such as pub/sub) can be built on top of it in your application if desired.

Probably I wasn't clear enough. I don't myself give any meaning to 'component".

OK, I'll try again :) @mbest has mentioned KO components composition. When we discuss composition we inevitably must say about component life cycle. Say, when I want to replace a DOM subtree attached to some div I want to clean bindings of previous model was attached to this div. If a component containing this model has subcomponents, whose responsibility is to cleanup subcomponents' bindings? And this is exactly that place where KO components can interfere with whole application framework (with own framework's sense of "modules" or "components" or any other suitable term denoting application parts with life cycle and ownership - read composition - tree).

@SteveSanderson

That's interesting. I suspect your use of the word "components" is quite different to what I'm proposing here. All I mean by "components" are things that have both views and viewmodels, and can be included asynchronously. This is very general-purpose and more specialised things (such as pub/sub) can be built on top of it in your application if desired.

Probably I wasn't clear enough. I don't myself give any meaning to 'component".

OK, I'll try again :) @mbest has mentioned KO components composition. When we discuss composition we inevitably must say about component life cycle. Say, when I want to replace a DOM subtree attached to some div I want to clean bindings of previous model was attached to this div. If a component containing this model has subcomponents, whose responsibility is to cleanup subcomponents' bindings? And this is exactly that place where KO components can interfere with whole application framework (with own framework's sense of "modules" or "components" or any other suitable term denoting application parts with life cycle and ownership - read composition - tree).

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 12, 2014

I would like to stress the requirements, already expressed by @EisenbergEffect about the need for well designed callbacks in all lifecycle events.
At moment two "holes" in the callbacks of the template binding lifecycle are ceating me a lot of problems, namely:

  1. The lack of a beforeBind callback, that is executed after the node has been added to the DOM but before its bindings are evaluated. Preparing nodes before applying bindings aometimes is a need. Moreover, when rendering recursive structures (like trees) a beforeBind is more useful than an afterRender since the afterRender is executed after the whole subtree under the node has already been built, so there is the risk to process the same node several times.
    Adding this callback is straightforward..I alredy have the code that do it

  2. When the beforeRemove is called, all nodes have been already cleaned(no more events or bindings attached to them), so one is forced to manipulate "Zombie" nodes. Abetter solution would be to leave the task of cleaning the node to the developer that write the beforeRemove.

I would like to stress the requirements, already expressed by @EisenbergEffect about the need for well designed callbacks in all lifecycle events.
At moment two "holes" in the callbacks of the template binding lifecycle are ceating me a lot of problems, namely:

  1. The lack of a beforeBind callback, that is executed after the node has been added to the DOM but before its bindings are evaluated. Preparing nodes before applying bindings aometimes is a need. Moreover, when rendering recursive structures (like trees) a beforeBind is more useful than an afterRender since the afterRender is executed after the whole subtree under the node has already been built, so there is the risk to process the same node several times.
    Adding this callback is straightforward..I alredy have the code that do it

  2. When the beforeRemove is called, all nodes have been already cleaned(no more events or bindings attached to them), so one is forced to manipulate "Zombie" nodes. Abetter solution would be to leave the task of cleaning the node to the developer that write the beforeRemove.

@chrizy

This comment has been minimized.

Show comment
Hide comment
@chrizy

chrizy Jan 14, 2014

Great! Anything that aligns knockout now to take advantage of ES6 features when they become available is the way to go.

chrizy commented Jan 14, 2014

Great! Anything that aligns knockout now to take advantage of ES6 features when they become available is the way to go.

@msteward

This comment has been minimized.

Show comment
Hide comment
@msteward

msteward Jan 16, 2014

I consider the AMD-helper plugin by @rniemeyer to be essential to all new KO projects I start so this proposal sounds like a great way forward for the library

I consider the AMD-helper plugin by @rniemeyer to be essential to all new KO projects I start so this proposal sounds like a great way forward for the library

@mbest

This comment has been minimized.

Show comment
Hide comment
@mbest

mbest Jan 20, 2014

Member

Following the discussion here, I decided to add embedded attribute bindings to Knockout.Punches. It must be enabled specifically (it's not turned on by enableAll). I haven't done any performance testing with it, so I'd be interested in what others find.

Member

mbest commented Jan 20, 2014

Following the discussion here, I decided to add embedded attribute bindings to Knockout.Punches. It must be enabled specifically (it's not turned on by enableAll). I haven't done any performance testing with it, so I'd be interested in what others find.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 20, 2014

It would be nice if the ko.components.register method might accept not only a ViewModel, but also a function that should return a ViewModel. Such a function should be passed both the component name and its data.
A similar function would give the opportunity to "instantiate" the component, adapting it to its context, in a way that is adequate for a MVVM pattern, that is by operating on the model, and then reflecting the changes on the UI.
Other frameworks like Angular js, are more Html centric than ViewModel centric, this make easier the implementation of simple applications, but is not adequate to complex business application.
I hope that the furure knockout library, would allow the implementation of ViewModel centric SPA frameworks,

It would be nice if the ko.components.register method might accept not only a ViewModel, but also a function that should return a ViewModel. Such a function should be passed both the component name and its data.
A similar function would give the opportunity to "instantiate" the component, adapting it to its context, in a way that is adequate for a MVVM pattern, that is by operating on the model, and then reflecting the changes on the UI.
Other frameworks like Angular js, are more Html centric than ViewModel centric, this make easier the implementation of simple applications, but is not adequate to complex business application.
I hope that the furure knockout library, would allow the implementation of ViewModel centric SPA frameworks,

@EisenbergEffect

This comment has been minimized.

Show comment
Hide comment
@EisenbergEffect

EisenbergEffect Jan 20, 2014

Perhaps you should look at Durandal? It can do exactly what you are asking for here.

Perhaps you should look at Durandal? It can do exactly what you are asking for here.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 20, 2014

Rob, I already know Durandall. My preoccupation is for the future....and we all know that the future(included the future of Durandall) is strictly tied to the choices that will be operated in the new of Knockout.js version. It appears that there is a general intention to move in a direction similar to Angular.js, since this framework is easier to use.... However, as already said an Html centric approach(like the one of Angular.js) is not adequate for business applications.

Rob, I already know Durandall. My preoccupation is for the future....and we all know that the future(included the future of Durandall) is strictly tied to the choices that will be operated in the new of Knockout.js version. It appears that there is a general intention to move in a direction similar to Angular.js, since this framework is easier to use.... However, as already said an Html centric approach(like the one of Angular.js) is not adequate for business applications.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Jan 20, 2014

Well I really hope Angular is not the main motivation for the proposed changes as it has a lot of issues.
frankabbruzzese: So you would prefer to compose components in JS? Or at least having the choice to do so? A bit like react.js perhaps?

Well I really hope Angular is not the main motivation for the proposed changes as it has a lot of issues.
frankabbruzzese: So you would prefer to compose components in JS? Or at least having the choice to do so? A bit like react.js perhaps?

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 20, 2014

AlexGalays: I would like to have the choice to do so...... The point here is not composing in Js or in Html, but mantaining separation of concerns between UI staffs and logical staffs. Both Js and Html composition are needed!
There are components, that are pure UI staffs, like a datapicker widget, for instance. They are better composed directly in the DOM. However, there are also more complex components that contains a lot of logics inside them, Such components play the role of "virtual pages" of a SPA more than simple widgets. Now, such components must be "prepared", or better "instantiated" in javascript, since their instantiation is a logic staff, not a pure UI staff....UI staffs instamtiation must automatically come from the bindings that connect the ViewModel with the template.
No need for other frameworks, my company already implemented a complete SPA Framework, based on knockout.js....It is enough for me that knockout.js will continue mantaining a correct MVVM approach....and doesn't move too much in a different direction :)

AlexGalays: I would like to have the choice to do so...... The point here is not composing in Js or in Html, but mantaining separation of concerns between UI staffs and logical staffs. Both Js and Html composition are needed!
There are components, that are pure UI staffs, like a datapicker widget, for instance. They are better composed directly in the DOM. However, there are also more complex components that contains a lot of logics inside them, Such components play the role of "virtual pages" of a SPA more than simple widgets. Now, such components must be "prepared", or better "instantiated" in javascript, since their instantiation is a logic staff, not a pure UI staff....UI staffs instamtiation must automatically come from the bindings that connect the ViewModel with the template.
No need for other frameworks, my company already implemented a complete SPA Framework, based on knockout.js....It is enough for me that knockout.js will continue mantaining a correct MVVM approach....and doesn't move too much in a different direction :)

@gaydenko

This comment has been minimized.

Show comment
Hide comment
@gaydenko

gaydenko Jan 20, 2014

that knockout.js will continue mantaining a correct MVVM approach....and doesn't move too much in a different direction :)

Very much the same! And I know that golden rule: "deprecate more than add" ;)

that knockout.js will continue mantaining a correct MVVM approach....and doesn't move too much in a different direction :)

Very much the same! And I know that golden rule: "deprecate more than add" ;)

@gaevoy

This comment has been minimized.

Show comment
Hide comment
@gaevoy

gaevoy Jan 21, 2014

I disagree to turn Knockout into Angular-like all-included library. Let it stay simple and flexible as core library with ability to extend.
For example, text interpolation will decrease performance because it performs regex replacement, etc.
My goal is to keep Knockout be as quick as possible, if you want to use some feature, please use plugins.

gaevoy commented Jan 21, 2014

I disagree to turn Knockout into Angular-like all-included library. Let it stay simple and flexible as core library with ability to extend.
For example, text interpolation will decrease performance because it performs regex replacement, etc.
My goal is to keep Knockout be as quick as possible, if you want to use some feature, please use plugins.

@gaevoy

This comment has been minimized.

Show comment
Hide comment
@gaevoy

gaevoy Jan 21, 2014

Feature request,
CSS Transition animation support for all bindings (if, with, foreach, visible).
For instance, knockout can assign some CSS classes during DOM operation so animation will be set in separated css file

gaevoy commented Jan 21, 2014

Feature request,
CSS Transition animation support for all bindings (if, with, foreach, visible).
For instance, knockout can assign some CSS classes during DOM operation so animation will be set in separated css file

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 21, 2014

@gaevoy,
At moment for template related bindings(if, with, template, foreach) you may use beforeRemove and afterRender to trigger transition animations

@gaevoy,
At moment for template related bindings(if, with, template, foreach) you may use beforeRemove and afterRender to trigger transition animations

@stalniy

This comment has been minimized.

Show comment
Hide comment
@stalniy

stalniy Jan 22, 2014

Regarding interpolation syntax: I believe that it'd better to implement custom templateSource which is able to compile html with iterpolation syntax to html with regular knockout bindings; for implementation we need only nodejs-dom-parser and logic from @mbest knockout.punches plugin. In this case we will have desired performance level and will be able to simplify our templates with interpolation syntax

stalniy commented Jan 22, 2014

Regarding interpolation syntax: I believe that it'd better to implement custom templateSource which is able to compile html with iterpolation syntax to html with regular knockout bindings; for implementation we need only nodejs-dom-parser and logic from @mbest knockout.punches plugin. In this case we will have desired performance level and will be able to simplify our templates with interpolation syntax

@gaevoy

This comment has been minimized.

Show comment
Hide comment
@gaevoy

gaevoy Jan 22, 2014

@frankabbruzzese you are correct according to beforeRemove and afterRender callbacks and CSS Transition animation.
But it is not so easy to implement all possible transitions in a plugin unless wrap template binding (bad idea). Moreover, syntax to describe some animation now is ugly. I will show below:

<div data-bind="template: {if: visible, beforeRemove: ko.aminations.fadeIn.beforeRemove, afterRender: ko.aminations.fadeIn.afterRender}">
</div>

or how can it be much more simpler

<div data-bind="if: visible" class="fade-in">
</div>

Technicality, each binding (template-based: if, with, foreach) will add css classes: if-appearing, if-appeared, if-disappearing, foreach-item-appearing, etc. So every possible animation can be written in separated css file. In this case easy to apply animation for all if bindings of application, or inside some css selector.
It would be great to have this feature on the board. For me it is more preferable than another features for ko 2014.
To discuss new thread created #1287

gaevoy commented Jan 22, 2014

@frankabbruzzese you are correct according to beforeRemove and afterRender callbacks and CSS Transition animation.
But it is not so easy to implement all possible transitions in a plugin unless wrap template binding (bad idea). Moreover, syntax to describe some animation now is ugly. I will show below:

<div data-bind="template: {if: visible, beforeRemove: ko.aminations.fadeIn.beforeRemove, afterRender: ko.aminations.fadeIn.afterRender}">
</div>

or how can it be much more simpler

<div data-bind="if: visible" class="fade-in">
</div>

Technicality, each binding (template-based: if, with, foreach) will add css classes: if-appearing, if-appeared, if-disappearing, foreach-item-appearing, etc. So every possible animation can be written in separated css file. In this case easy to apply animation for all if bindings of application, or inside some css selector.
It would be great to have this feature on the board. For me it is more preferable than another features for ko 2014.
To discuss new thread created #1287

@mariusGundersen

This comment has been minimized.

Show comment
Hide comment
@mariusGundersen

mariusGundersen Jan 22, 2014

Contributor

@gaevoy This should probably be its own issue. I like the proposed solution, but I would like to add my thoughts to the general idea, just not in this thread.

Contributor

mariusGundersen commented Jan 22, 2014

@gaevoy This should probably be its own issue. I like the proposed solution, but I would like to add my thoughts to the general idea, just not in this thread.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 22, 2014

@gaevoy,
your solution is interesting, but, one has to pay attention also to the coordination between the css animation and possible beforeRemove and/or afterRender that might be present.
A good solution would be applying the animation automatically only if there is no afterRender (or beforeRender). In case there is an afterRender (or beforeRender) the animation should be started manually, by calling a ko method(that do all the job) inside the afterRender (or beforeRemove), so that animation may be always started with the right timing.

@gaevoy,
your solution is interesting, but, one has to pay attention also to the coordination between the css animation and possible beforeRemove and/or afterRender that might be present.
A good solution would be applying the animation automatically only if there is no afterRender (or beforeRender). In case there is an afterRender (or beforeRender) the animation should be started manually, by calling a ko method(that do all the job) inside the afterRender (or beforeRemove), so that animation may be always started with the right timing.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 22, 2014

About the whole stuff, of upgrading knoickout.js with text interpolation and, with some support for web components, one must also consider that renouncing to a similar update might kill knockout.js, in favour of other frameworks. In fact, most of developers, just want "put existing things together" and would prefer frameworks that offers more options :). This in turn might push knockout.js down causing it loose the competition with other frameworks. Defining everything, in plug-ins doesn't resolve the issue, since the issue concerns mainly the "reputation" of knockout.js itself more than strictly technical problems.
For this reason, I vote for both the text interpolation and the web components related upgrades, but oth updates should be very, very basic, without entering too much in the territory of complete SPA frameworks, and should not impose choices on communication libraries to use for downloading templates and js, on possible routing algorithms, etc. This, means furnishing just basic services with the maximum flexibility, Thus,... a lot od life-cycle callbacks, and customization points.

About the whole stuff, of upgrading knoickout.js with text interpolation and, with some support for web components, one must also consider that renouncing to a similar update might kill knockout.js, in favour of other frameworks. In fact, most of developers, just want "put existing things together" and would prefer frameworks that offers more options :). This in turn might push knockout.js down causing it loose the competition with other frameworks. Defining everything, in plug-ins doesn't resolve the issue, since the issue concerns mainly the "reputation" of knockout.js itself more than strictly technical problems.
For this reason, I vote for both the text interpolation and the web components related upgrades, but oth updates should be very, very basic, without entering too much in the territory of complete SPA frameworks, and should not impose choices on communication libraries to use for downloading templates and js, on possible routing algorithms, etc. This, means furnishing just basic services with the maximum flexibility, Thus,... a lot od life-cycle callbacks, and customization points.

@stalniy

This comment has been minimized.

Show comment
Hide comment
@stalniy

stalniy Jan 23, 2014

I created a plunker which shows that it's quite easy to implement custom template source based on knockout.punches: http://plnkr.co/lqMivwn7y8lTvdRqHeRQ . Also I tried to compile html files using jsdom library for node.js : everything works fine, so it won't take too much effort to implement grunt task for this purpose.

stalniy commented Jan 23, 2014

I created a plunker which shows that it's quite easy to implement custom template source based on knockout.punches: http://plnkr.co/lqMivwn7y8lTvdRqHeRQ . Also I tried to compile html files using jsdom library for node.js : everything works fine, so it won't take too much effort to implement grunt task for this purpose.

@stalniy

This comment has been minimized.

Show comment
Hide comment
@stalniy

stalniy Jan 23, 2014

However I think if we mix both data-bind and interpolation syntax it will become a mass in our templates. Maybe, it should be possibility to use any binding using interpolation syntax, shouldn't it? Also I think it should be possible to combine few bindings, something like:

{{ foreach: templates; click: doSomething }}
 <a href="{{ url }}">{{ title | upper }}</a>
{{ /end }}

But some bindings requires to be bound on real DOM node and it makes things to be more complicated... As possible solution we can process DOM nodes as bindings (domNode.nodeName equals to binding name), for example:

<visible if="hasTemplates">
  {{ foreach: templates; click: doSomething }}
    <a href="{{ url }}">{{ title | upper }}</a>
  {{ /end }}
</visible>

But in this case we can apply only one binding to node.

Does anybody have suggestions how to go around this issues?

stalniy commented Jan 23, 2014

However I think if we mix both data-bind and interpolation syntax it will become a mass in our templates. Maybe, it should be possibility to use any binding using interpolation syntax, shouldn't it? Also I think it should be possible to combine few bindings, something like:

{{ foreach: templates; click: doSomething }}
 <a href="{{ url }}">{{ title | upper }}</a>
{{ /end }}

But some bindings requires to be bound on real DOM node and it makes things to be more complicated... As possible solution we can process DOM nodes as bindings (domNode.nodeName equals to binding name), for example:

<visible if="hasTemplates">
  {{ foreach: templates; click: doSomething }}
    <a href="{{ url }}">{{ title | upper }}</a>
  {{ /end }}
</visible>

But in this case we can apply only one binding to node.

Does anybody have suggestions how to go around this issues?

@gaevoy

This comment has been minimized.

Show comment
Hide comment
@gaevoy

gaevoy Jan 23, 2014

@stalniy, good idea to make all heavy replacement using nodejs. To continue this thought, my proposition is to build RequireJS plugin which will transform view and replace all Custom elements and Text interpolation with proper binding, for developing it will replace on client side otherwise by RequireJS Optimizer (r.js) on server side. One notice, you are obliged to load all your views by RequireJS in this case.

gaevoy commented Jan 23, 2014

@stalniy, good idea to make all heavy replacement using nodejs. To continue this thought, my proposition is to build RequireJS plugin which will transform view and replace all Custom elements and Text interpolation with proper binding, for developing it will replace on client side otherwise by RequireJS Optimizer (r.js) on server side. One notice, you are obliged to load all your views by RequireJS in this case.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 26, 2014

About performance of text interpolation. The main impact on performance is due to text interpolation located in templates (in line or not, or template that are part of components), since templates may be instantiated several times.
However, templates may be compiled during their first usage, thus avoiding any server side compilation...that would lead knockout.js far in the SPA territory.
Summing up, compilation on firts usage of all templates to transform any text interpolation into its equivalent data-bind syntax.
Such compilation may be performed either on the string form of the templates or after the template has been transformed in a DOM tree. Not sure what of the two approaches is faster, since regex processing is done by the browser but is more complex, while DOM tree navigation must be done in javascript.

About performance of text interpolation. The main impact on performance is due to text interpolation located in templates (in line or not, or template that are part of components), since templates may be instantiated several times.
However, templates may be compiled during their first usage, thus avoiding any server side compilation...that would lead knockout.js far in the SPA territory.
Summing up, compilation on firts usage of all templates to transform any text interpolation into its equivalent data-bind syntax.
Such compilation may be performed either on the string form of the templates or after the template has been transformed in a DOM tree. Not sure what of the two approaches is faster, since regex processing is done by the browser but is more complex, while DOM tree navigation must be done in javascript.

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jan 28, 2014

Contributor

@mbest - Would you be able to provide a bit more detail about scenarios you had in mind with the comment "I'd also like to see it support composition and inheritance."?

I've been looking at different kinds of composition/inheritance and the main one that's obviously incredibly useful is simply nesting (referencing one component from another).

As for inheritance, one technique that would certainly work would just be inheriting one viewmodel from a different component's viewmodel. However in a sense that violates the encapsulation of the component being inherited, because its API is intended for consumption by its template, not by other viewmodels. This might be OK if it's your component and you know how it works, but gets risky in more general cases.

If there are particular scenarios and kinds of inheritance you have in mind, could you let me know?

Contributor

SteveSanderson commented Jan 28, 2014

@mbest - Would you be able to provide a bit more detail about scenarios you had in mind with the comment "I'd also like to see it support composition and inheritance."?

I've been looking at different kinds of composition/inheritance and the main one that's obviously incredibly useful is simply nesting (referencing one component from another).

As for inheritance, one technique that would certainly work would just be inheriting one viewmodel from a different component's viewmodel. However in a sense that violates the encapsulation of the component being inherited, because its API is intended for consumption by its template, not by other viewmodels. This might be OK if it's your component and you know how it works, but gets risky in more general cases.

If there are particular scenarios and kinds of inheritance you have in mind, could you let me know?

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 29, 2014

@SteveSanderson ,
Actually, one might think of a more sophysticated way of defining inheritance between components that doesn't violate component encapsulation.

First, the inheriting component should inherit also the template. The inheriting component may change the appearence of the original template by changing the name of other components included in the template. Possible also that the inherited template is not used as it is but may be included inside another template defined in the inheriting component.

Second, The ViewModel of the inheriting component should not inherit all properties and methods of the ViewModel of the inherited component, but it should have available only some "protected data" of the inherited component. This way we dont break component encapsulation and we dont force the developer to know to much details of the inherited component. Moreover, if the inherited component is completely substituted from a different implementation that mantains the same public and protedted component data the inheriting component may continue working properly.

Third. In order to give a meaning to point 2 we must give a formal definition of what is public and what is protected in a component.
We already know what is public: the component name and the data passed as input to the component(in the data-bind mark-up). Thus, the simplest way of defining component protected data is as a further input object that is not passed in the component markup but that is handled by the javascript code that implements component inheritage.
More specifically, when a component declares to inherit from another it receives as output the object containing all protected data, that it may override, or use:

.....
....
var protectedData = ko.components.inheritFrom('people-list', params);
....
....

where "params" are the data to be passed to the constructor of the ViewModel of the inherited component.

The constructor of each component ViewModel should receive, not only the component input data but also a possible null object where it may store all component protected data:

function peopleList(component, params, protectedDataObject) {
     protectedDataObject = protectedDataObject || {};
     ......
      .....
}

When the constructor is invoked directly (not for creating an inheriting component) protectedDataObject is null, otherwise it is an object created by "ko.components.inheritFrom", and then returned to the caller of "ko.components.inheritFrom".

@SteveSanderson ,
Actually, one might think of a more sophysticated way of defining inheritance between components that doesn't violate component encapsulation.

First, the inheriting component should inherit also the template. The inheriting component may change the appearence of the original template by changing the name of other components included in the template. Possible also that the inherited template is not used as it is but may be included inside another template defined in the inheriting component.

Second, The ViewModel of the inheriting component should not inherit all properties and methods of the ViewModel of the inherited component, but it should have available only some "protected data" of the inherited component. This way we dont break component encapsulation and we dont force the developer to know to much details of the inherited component. Moreover, if the inherited component is completely substituted from a different implementation that mantains the same public and protedted component data the inheriting component may continue working properly.

Third. In order to give a meaning to point 2 we must give a formal definition of what is public and what is protected in a component.
We already know what is public: the component name and the data passed as input to the component(in the data-bind mark-up). Thus, the simplest way of defining component protected data is as a further input object that is not passed in the component markup but that is handled by the javascript code that implements component inheritage.
More specifically, when a component declares to inherit from another it receives as output the object containing all protected data, that it may override, or use:

.....
....
var protectedData = ko.components.inheritFrom('people-list', params);
....
....

where "params" are the data to be passed to the constructor of the ViewModel of the inherited component.

The constructor of each component ViewModel should receive, not only the component input data but also a possible null object where it may store all component protected data:

function peopleList(component, params, protectedDataObject) {
     protectedDataObject = protectedDataObject || {};
     ......
      .....
}

When the constructor is invoked directly (not for creating an inheriting component) protectedDataObject is null, otherwise it is an object created by "ko.components.inheritFrom", and then returned to the caller of "ko.components.inheritFrom".

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jan 29, 2014

Contributor

@frankabbruzzese - Thanks for those ideas! That's an interesting set of possibilities, but is dramatically more complex and opinionated than the sort of design I'm putting together as a base level. If those kinds of patterns happen to emerge through real-world use then that's great, but I would prefer to leave patterns of inheritance open rather than enforcing that sort of sophistication in the library.

Contributor

SteveSanderson commented Jan 29, 2014

@frankabbruzzese - Thanks for those ideas! That's an interesting set of possibilities, but is dramatically more complex and opinionated than the sort of design I'm putting together as a base level. If those kinds of patterns happen to emerge through real-world use then that's great, but I would prefer to leave patterns of inheritance open rather than enforcing that sort of sophistication in the library.

@frankabbruzzese

This comment has been minimized.

Show comment
Hide comment
@frankabbruzzese

frankabbruzzese Jan 30, 2014

@SteveSanderson , I agree with you 👍 It was just a further input to help in figuring out the various possibilities. A similar sophistication is better provided, for instance, in a plug-in containing a library of components.

@SteveSanderson , I agree with you 👍 It was just a further input to help in figuring out the various possibilities. A similar sophistication is better provided, for instance, in a plug-in containing a library of components.

@mbest

This comment has been minimized.

Show comment
Hide comment
@mbest

mbest Jan 30, 2014

Member

Would you be able to provide a bit more detail about scenarios you had in mind with the comment "I'd also like to see it support composition and inheritance."?

I think you've addressed those issues sufficiently in the spec (#1290). I do have some comments and suggestions on the spec, which I will add shortly.

Member

mbest commented Jan 30, 2014

Would you be able to provide a bit more detail about scenarios you had in mind with the comment "I'd also like to see it support composition and inheritance."?

I think you've addressed those issues sufficiently in the spec (#1290). I do have some comments and suggestions on the spec, which I will add shortly.

@stalniy

This comment has been minimized.

Show comment
Hide comment
@stalniy

stalniy Apr 5, 2014

I think that it might be interesting for everyone in this thread to know that I implemented mustached binding syntax interpolator which is available on github. Any feedback is very appreciated.

stalniy commented Apr 5, 2014

I think that it might be interesting for everyone in this thread to know that I implemented mustached binding syntax interpolator which is available on github. Any feedback is very appreciated.

@RoyJacobs

This comment has been minimized.

Show comment
Hide comment
@RoyJacobs

RoyJacobs Jun 13, 2014

Contributor

I'm a bit late to the party, sorry about that. One thing I'm missing in the component spec is the capability to add content within the markup tags, similar to what Angular would call transclusion.

For example: In the case of a dialog component, this transcluded markup could contain the contents of the dialog whereas the params would be used for the dialog title and so on.

Contributor

RoyJacobs commented Jun 13, 2014

I'm a bit late to the party, sorry about that. One thing I'm missing in the component spec is the capability to add content within the markup tags, similar to what Angular would call transclusion.

For example: In the case of a dialog component, this transcluded markup could contain the contents of the dialog whereas the params would be used for the dialog title and so on.

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jun 18, 2014

Contributor

@RoyJacobs - yep, great point. This has been considered in the design, and will be pretty simple for us to implement support for. All we have to do is add an extra property, originalChildNodes (or whatever), to the componentInfo parameter that gets passed to createViewModel callbacks.

This can go into 3.3 if there's any demand for it (and I suspect there will be).

Contributor

SteveSanderson commented Jun 18, 2014

@RoyJacobs - yep, great point. This has been considered in the design, and will be pretty simple for us to implement support for. All we have to do is add an extra property, originalChildNodes (or whatever), to the componentInfo parameter that gets passed to createViewModel callbacks.

This can go into 3.3 if there's any demand for it (and I suspect there will be).

@SteveSanderson

This comment has been minimized.

Show comment
Hide comment
@SteveSanderson

SteveSanderson Jul 22, 2014

Contributor

I'm going to close this issue now, because the 3.2.0 feature is fully implemented, and the thread is so long we're never going to make sense of it in the future! I think the remaining feature request here - "content within custom element tags" - is now covered in a different issue, so we're not going to forget that.

Contributor

SteveSanderson commented Jul 22, 2014

I'm going to close this issue now, because the 3.2.0 feature is fully implemented, and the thread is so long we're never going to make sense of it in the future! I think the remaining feature request here - "content within custom element tags" - is now covered in a different issue, so we're not going to forget that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment