Skip to content

Commit

Permalink
Improve component registration docs
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSanderson committed Jun 27, 2014
1 parent db643d1 commit 794c2e1
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 18 deletions.
2 changes: 1 addition & 1 deletion _includes/documentation-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ <h1>Creating custom bindings</h1>
<h1>Components</h1>
<ol>
<li><a href="{{ page.pathprefix }}documentation/component-overview.html">Overview: What <em>components</em> and <em>custom elements</em> offer</a></li>
<li><a href="{{ page.pathprefix }}documentation/component-definitions.html">Defining and registering components</a></li>
<li><a href="{{ page.pathprefix }}documentation/component-registration.html">Defining and registering components</a></li>
<li><a href="{{ page.pathprefix }}documentation/component-binding.html">The <code>component</code> binding</a></li>
<li><a href="{{ page.pathprefix }}documentation/component-custom-elements.html">Using custom elements</a></li>
<li><a href="{{ page.pathprefix }}documentation/component-loader.html">Advanced: Custom component loaders</a></li>
Expand Down
2 changes: 1 addition & 1 deletion documentation/component-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ If you open your browser developer tools' **Network** inspector before your firs

More more detailed information, see:

* [Defining and registering components](component-definitions.html)
* [Defining and registering components](component-registration.html)
* [Using the `component` binding](component-binding.html)
* [Using custom elements](component-custom-elements.html)
* [Advanced: Custom component loaders](component-loader.html)
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
---
layout: documentation
title: Component definitions
title: Component registration
---

For Knockout to be able to load and instantiate your components, you must register them using `ko.components.register`, providing a configuration as described here.

*Note: As an alternative, it's possible to implement a [custom component loader](component-loader.html) that fetches components by your own conventions instead of explicit configuration.*

* [Table of contents injected here]
{:toc}

## Registering components as a viewmodel/template pair

You can register a component as follows:
Expand All @@ -26,7 +29,7 @@ If no viewmodel is given, the component is treated as a simple block of HTML tha

Viewmodels can be specified in any of the following forms:

#### 1. A constructor function
#### A constructor function

function SomeComponentViewModel(params) {
// 'params' is an object whose key/value pairs are the parameters
Expand All @@ -43,7 +46,7 @@ Viewmodels can be specified in any of the following forms:

Knockout will invoke your constructor once for each instance of the component, producing a separate viewmodel object for each. Properties on the resulting object or its prototype chain (e.g., `someProperty` and `doSomething` in the example above) are available for binding in the component's view.

#### 2. A shared object instance
#### A shared object instance

If you want all instances of your component to share the same viewmodel object instance (which is not usually desirable):

Expand All @@ -56,7 +59,7 @@ If you want all instances of your component to share the same viewmodel object i

Note that it's necessary to specify `viewModel: { instance: object }`, and not just `viewModel: object`. This differentiates from the other cases below.

#### 3. A `createViewModel` factory function
#### A `createViewModel` factory function

If you want to run any setup logic on the associated element before it is bound to the viewmodel, or use arbitrary logic to decide which viewmodel class to instantiate:

Expand All @@ -78,7 +81,7 @@ If you want to run any setup logic on the associated element before it is bound

Note that, typically, it's best to perform direct DOM manipulation only through [custom bindings](custom-bindings.html) rather than acting on `componentInfo.element` from inside `createViewModel`. This leads to more modular, reusable code.

#### 4. An AMD module to be loaded asynchronously, on demand
#### An AMD module whose value describes a viewmodel

If you have an AMD loader (such as [require.js](http://requirejs.org/)) already in your page, then you can use it to fetch a viewmodel. For more details about how this works, see [how Knockout loads components via AMD](#how-knockout-loads-components-via-amd) below. Example:

Expand Down Expand Up @@ -132,13 +135,13 @@ The returned AMD module object can be in any of the forms allowed for viewmodels

Templates can be specified in any of the following forms. The most commonly useful are [existing element IDs](#an-existing-element-id) and [AMD modules](#an-amd-module-whose-value-describes-a-template).

#### 1. An existing element ID
#### An existing element ID

For example, the following element:

<template id='my-component-template'>
<h1 data-bind='text: title'></h1>
<button data-bind='click: doSomething' />
<button data-bind='click: doSomething'>Click me right now</button>
</template>

... can be used as the template for a component by specifying its ID:
Expand All @@ -152,7 +155,7 @@ Note that only the nodes *inside* the specified element will be cloned into each

You're not limited to using `<template>` elements, but these are convenient (on browsers that support them) since they don't get rendered on their own. Any other element type works too.

#### 2. An existing element instance
#### An existing element instance

If you have a reference to a DOM element in your code, you can use it as a container for template markup:

Expand All @@ -165,17 +168,17 @@ If you have a reference to a DOM element in your code, you can use it as a conta

Again, only the nodes *inside* the specified element will be cloned for use as the component's template.

#### 3. A string of markup
#### A string of markup

ko.components.register('my-component', {
template: '<h1 data-bind="text: title"></h1>\
<button data-bind="click: doSomething" />',
<button data-bind="click: doSomething">Clickety</button>',
viewModel: ...
});

This is mainly useful when building configurations programmatically, or as a build system output that packages components for distribution, since it's not very convenient to manually edit HTML as a JavaScript string literal.
This is mainly useful when you're fetching the markup from somewhere programmatically (e.g., [AMD - see below](#a-recommended-amd-module-pattern)), or as a build system output that packages components for distribution, since it's not very convenient to manually edit HTML as a JavaScript string literal.

#### 4. An array of DOM nodes
#### An array of DOM nodes

If you're building configurations programmatically and you have an array of DOM nodes, you can use them as a component template:

Expand All @@ -192,7 +195,7 @@ If you're building configurations programmatically and you have an array of DOM

In this case, all the specified nodes (and their descendants) will be cloned and concatenated into each copy of the component that gets instantiated.

#### 5. A document fragment
#### A document fragment

If you're building configurations programmatically and you have a `DocumentFragment` object, you can use it as a component template:

Expand All @@ -203,7 +206,7 @@ If you're building configurations programmatically and you have a `DocumentFragm

Since document fragments can have multiple top-level nodes, the *entire* document fragment (not just descendants of top-level nodes) is treated as the component template.

#### 6. An AMD module whose value describes a template
#### An AMD module whose value describes a template

If you have an AMD loader (such as [require.js](http://requirejs.org/)) already in your page, then you can use it to fetch a template. For more details about how this works, see [how Knockout loads components via AMD](#how-knockout-loads-components-via-amd) below. Example:

Expand Down Expand Up @@ -232,11 +235,15 @@ When you load a viewmodel or template via `require` declarations, e.g.,

...all Knockout does is call `require('some/module/name', callback)` and `require('text!some-template.html', callback)`, and uses the asynchronously-returned objects as the viewmodel and template definitions. So,

* **This does not take a strict dependency on [require.js](http://requirejs.org/)** or any other particular module loader. *Any* module loader that provides an AMD-style `require` API will do. If you want to integrate with a module loader whose API is different, you can implement a [custom loader](component-loader.html).
* **This does not take a strict dependency on [require.js](http://requirejs.org/)** or any other particular module loader. *Any* module loader that provides an AMD-style `require` API will do. If you want to integrate with a module loader whose API is different, you can implement a [custom component loader](component-loader.html).
* **Knockout does not interpret the module name** in any way - it merely passes it through to `require()`. So of course Knockout does not know or care about where your module files are loaded from. That's up to your AMD loader and how you've configured it.
* **Knockout doesn't know or care whether your AMD modules are anonymous or not**. Typically we find it's most convenient for components to be defined as anonymous modules, but that concern is entirely separate from KO.

Note that **Knockout does not call `require(moduleName, ...)` until your component is being instantiated**. This is how components get loaded on demand, not up front. Of course, if the AMD module was already loaded (e.g., in a preloaded bundle) then the `require` call will not trigger any additional HTTP requests, so you can control what is preloaded and what is loaded on demand.
#### AMD modules are loaded only on demand

Knockout does not call `require(moduleName, ...)` until your component is being instantiated. This is how components get loaded on demand, not up front.

For example, if your component is inside some other element with an [`if` binding](if-binding.html) (or another control flow binding), then it will not cause the AMD module to be loaded until the `if` condition is true. Of course, if the AMD module was already loaded (e.g., in a preloaded bundle) then the `require` call will not trigger any additional HTTP requests, so you can control what is preloaded and what is loaded on demand.


## Registering components as a single AMD module
Expand Down

0 comments on commit 794c2e1

Please sign in to comment.