The component Binding

The component binding injects a specified component into an element using the data-bind syntax. This is an alternative to using a <custom-element></custom-element>.

Example

<h4>First instance, without parameters</h4>
<div data-bind='component: "message-editor"'></div>

<h4>Second instance, passing parameters</h4>
<div data-bind='component: {
  name: "message-editor",
  params: { initialText: "Hello, world!" }
}'></div>
fw.components.register('message-editor', {
  viewModel: function(params) {
    this.text = fw.observable(params && params.initialText || '');
  },
  template: 'Message: <input data-bind="value: text" /> '
          + '(length: <span data-bind="text: text().length"></span>)'
});

fw.applyBindings();

Note

In more realistic cases, you would typically load component viewmodels and templates from external files, instead of hardcoding them into the registration. See an example and registration documentation.

Usage

There are two ways to use the component binding:

  • Shorthand syntax

    If you pass just a string, it is interpreted as a component name. The named component is then injected without supplying any parameters to it. Example:

    <div data-bind='component: "my-component"'></div>
    

    The shorthand value can also be observable. In this case, if it changes, the component binding will dispose the old component instance, and inject the newly-referenced component. Example:

    <div data-bind='component: observableWhoseValueIsAComponentName'></div>
    
  • Full syntax

    To supply parameters to the component, pass an object with the following properties:

    • name

      The name of the component to inject. Again, this can be observable.

    • params

      An object that will be passed on to the component. Typically this is a key-value object containing multiple parameters, and is typically received by the component's viewmodel constructor.

    <div data-bind='component: {
      name: "shopping-cart",
      params: { mode: "detailed-list", items: productsList }
    }'></div>
    

    Note

    Whenever a component is removed (either because the name observable changed, or because an enclosing control-flow binding removed the entire element), the removed component is disposed.

Template-only components

Components usually have viewmodels, but they don't necessarily have to. A component can specify just a template.

In this case, the object to which the component's view is bound is the params object that you passed to the component binding. Example:

fw.components.register('special-offer', {
  template: '<div class="offer-box" data-bind="text: productName"></div>'
});

... can be injected with:

<div data-bind='component: {
  name: "special-offer-callout",
  params: { productName: someProduct.name }
}'></div>

... or, more conveniently, as a custom element:

<special-offer params='productName: someProduct.name'></special-offer>

Containerless Usage

Sometimes you may want to inject a component into a view without using an extra container element. You can do this using containerless control flow syntax, which is based on comment tags. For example,

<!-- ko component: "message-editor" -->
<!-- /ko -->

... or passing parameters:

<!-- ko component: {
  name: "message-editor",
  params: { initialText: "Hello, world!", otherParam: 123 }
} -->
<!-- /ko -->

The <!-- ko --> and <!-- /ko --> comments act as start/end markers, defining a "virtual element" that contains the markup inside. Footwork understands this virtual element syntax and binds as if you had a real container element.

Passing Markup To Components

The element you attach a component binding to may contain further markup. For example,

<div data-bind="component: { name: 'my-special-list', params: { items: someArrayOfPeople } }">
  <!-- Look, here's some arbitrary markup. By default it gets stripped out
       and is replaced by the component output. -->
  The person <em data-bind="text: name"></em> is <em data-bind="text: age"></em> years old.
</div>

Although the DOM nodes in this element will be stripped out and not bound by default, they are not lost. Instead, they are supplied to the component (in this case, my-special-list), which can include them in its output however it wishes.

This is useful if you want to build components that represent "container" UI elements, such as grids, lists, dialogs, or tab sets, which need to inject and bind arbitrary markup into a common structure. See a complete example for custom elements, which also works without custom elements using the syntax shown above.