Skip to content
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
Closed

Components and custom elements #1273

SteveSanderson opened this issue Jan 11, 2014 · 74 comments
Labels

Comments

@SteveSanderson
Copy link
Contributor

@SteveSanderson 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
Copy link

@jakcharlton jakcharlton commented 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)

@AlexZeitler
Copy link

@AlexZeitler AlexZeitler commented Jan 11, 2014

I like it

@thunklife
Copy link

@thunklife thunklife commented 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.

@SteveSanderson
Copy link
Contributor Author

@SteveSanderson 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
Copy link

@chanan 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
Copy link

@JeffSpies JeffSpies commented 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.

@AnthonyPAlicea
Copy link

@AnthonyPAlicea AnthonyPAlicea commented 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.

@thunklife
Copy link

@thunklife thunklife commented 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?

@brigand
Copy link

@brigand 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
Copy link

@garvincasimir garvincasimir commented 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.

@brettwgreen
Copy link

@brettwgreen brettwgreen commented 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.

@milutinovici
Copy link

@milutinovici milutinovici commented 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.

@mariusGundersen
Copy link
Contributor

@mariusGundersen 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
Copy link

@johnnyreilly johnnyreilly commented Jan 11, 2014

Very exciting - I like it all!

@chitza
Copy link

@chitza 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
Copy link
Member

@mbest 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
Copy link
Member

@mbest 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
Copy link

@craigktreasure craigktreasure commented 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.

@gaydenko
Copy link

@gaydenko gaydenko commented 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 :)

@PrivateJson
Copy link

@PrivateJson PrivateJson commented 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!

@stalniy
Copy link

@stalniy 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
Copy link

@chester89 chester89 commented 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 👍

@frankabbruzzese
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@AlexGalays
Copy link

@AlexGalays AlexGalays commented 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

@rniemeyer
Copy link
Member

@rniemeyer 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
Copy link
Contributor

@mariusGundersen 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
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@dylanized
Copy link

@dylanized dylanized commented 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!!

@rniemeyer
Copy link
Member

@rniemeyer 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).

@gaevoy
Copy link

@gaevoy 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
Copy link

@frankabbruzzese frankabbruzzese commented Jan 21, 2014

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

@stalniy
Copy link

@stalniy 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
Copy link

@gaevoy 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
Copy link
Contributor

@mariusGundersen 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
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@frankabbruzzese
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@stalniy
Copy link

@stalniy 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
Copy link

@stalniy 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
Copy link

@gaevoy 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
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@SteveSanderson
Copy link
Contributor Author

@SteveSanderson 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
Copy link

@frankabbruzzese frankabbruzzese commented 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
Copy link
Contributor Author

@SteveSanderson 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
Copy link

@frankabbruzzese frankabbruzzese commented 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.

@mbest
Copy link
Member

@mbest 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
Copy link

@stalniy 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
Copy link
Contributor

@RoyJacobs 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
Copy link
Contributor Author

@SteveSanderson 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
Copy link
Contributor Author

@SteveSanderson 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
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.