KnockoutJS - Components


Advertisements

Components are a huge way of organizing the UI code for structuring a large application and promoting code reusability.

It is inherited or nested from other component. For loading and configuration, it defines its own conventions or logic.

It is packaged to reuse throughout the application or the project. Represents the complete sections of application or small controls/widgets. It can be loaded or preloaded on demand.

Component Registration

Components can register using the ko.components.register() API. It helps to load and represent the components in KO. Component name with configuration is expected for registration. The configuration specifies how to determine the viewModel and template.

Syntax

Components can be registered as follows −

ko.components.register('component-name', {
   viewModel: {...},    //function code
   template: {....)	//function code
});
  • The component-name can be any nonempty string.

  • viewModel is optional, and can take any of the viewModel formats listed in the next sections.

  • template is required, and can take any of the template formats listed in the next sections.

Stating a ViewModel

Following table lists the viewModel formats that can be used to register the components.

Sr.No. viewModel Forms & Description
1

constructor function

It creates a separate viewModel object for each component. The object or function is used to bind in components view.

function SomeComponentViewModel(params) {
   this.someProperty = params.something;
}
ko.components.register('component name', {
   viewModel: SomeComponentViewModel,
   template: ...
});
2

shared object instance

The viewModel object instance is shared. The instance property is passed to use the object directly.

var sharedViewModelInstance = { ... };

ko.components.register('component name', {
   viewModel: { instance: sharedViewModelInstance },
   template: ...
});
3

createViewModel

It calls a function which acts as a factory and can be used as view model that can return an object.

ko.components.register('component name', {  
   viewModel: {  
      createViewModel: function (params, componentInfo) {  
         ...       //function code  
         ...
      }  
   },  
   template: ....  
});
4

AMD module

It is a module format for defining modules where module and dependencies both are loaded asynchronously.

ko.components.register('component name', {
   viewModel: { require: 'some/module/name' },
   template: ...
});

define(['knockout'], function(ko) {
   function MyViewModel() {
      // ...
   }

   return MyViewModel;
});

Stating a Template

Following table lists the template formats that can be used to register the components.

Sr.No. Template Forms
1

element ID

ko.components.register('component name', {
   template: { element: 'component-template' },
   viewModel: ...
});
2

element instance

var elemInstance = document.getElementById('component-template');

ko.components.register('component name', {
   template: { element: elemInstance },
   viewModel: ...
});
3

string of markup

ko.components.register('component name', {
   template: '<input data-bind = "value: yourName" />\
      <button data-bind = "click: addEmp">Add Emp </button>',
   viewModel: ...
});
4

DOM nodes

var emp = [
   document.getElementById('node 1'),
   document.getElementById('node 2'),
];

ko.components.register('component name', {
   template: emp,
   viewModel: ...
});
5

document fragement

ko.components.register('component name', {
   template: someDocumentFragmentInstance,
   viewModel: ...
});
6

AMD module

ko.components.register('component name', {
   template: { require: 'some/template' },
   viewModel: ...
});

Components Registered as a Single AMD Module

The AMD module can register a component by itself without using viewModel/template pair.

ko.components.register('component name',{ require: 'some/module'});

Component Binding

There are two ways of component binding.

  • Full syntax − It passes the parameter and object to the component. It can pass using the following properties.

    • name − It adds the component name.

    • params − It can pass multiple parameters in object on the component.

<div data-bind='component: {
   name: "tutorials point",
   params: { mode: "detailed-list", items: productsList }
}'>
</div>
  • Shorthand syntax − It passes the string as a component name and it does not include parameter in it.

<div data-bind = 'component: "component name"'></div>
  • Template-only components − Components can only define template without specifing the viewModel.

ko.components.register('component name', {
   template:'<input data-bind = "value: someName" />,
});
  • Using Component without a container element − Components can be used without using extra container element. This can be done using containerless flow control which is similar as the comment tag.

<!--ko.component: ""-->
<!--/ko-->

Custom Element

Custom element is a way for rendering a component. Here, you can directly write a selfdescriptive markup element name instead of defining a placeholder, where the components are binded through it.

<products-list params = "name: userName, type: userType"></products-list>

Passing Parameter

params attribute is used to pass the parameter to component viewModel. It is similar to data-bind attribute. The contents of the params attribute are interpreted like a JavaScript object literal (just like a data-bind attribute), so you can pass arbitrary values of any type. It can pass the parameter in following ways −

  • Communication between parent and child components − The component is not instantiated by itself so the viewmodel properties are referred from outside of the component and thus would be received by child component viewmodel. For example, you can see in the following syntax that ModelValue is the parent viewmodel, which is received by child viewModel constructor ModelProperty.

  • Passing observable expressions − It has three values in params parameter.

    • simpleExpression − It is a numeric value. It does not involve any observables.

    • simpleObservable − It is an instance that is defined on parent viewModel. The parent viewModel will automatically get the changes on observable done by child viewModel.

    • observableExpression − Expression reads the observable when the expression is evaluated by itself. When the observable value changes, then the result of expression can also changs over time.

We can pass the parameters as follows −

<some-component
   params = 'simpleExpression: 1 + 1,
      simpleObservable: myObservable,
      observableExpression: myObservable() + 1'>
</some-component>

We can pass the parameters in viewModel as follows −

<some-component
   params = 'objectValue:{a: 3, b: 2},
      dateValue: new date(),
      stringValue: "Hi",
      numericValue:123,
      boolValue: true/false,
      ModelProperty: ModelValue'>
</some-component>

Passing Markup into Components

The received markup is used to create a component and is selected as a part of the output. Following nodes are passed as part of the output in the component template.

template: { nodes: $componentTemplateNodes }

Controlling custom element tag names

The names which you register in the components using ko.components.register, the same name corresponds to the custom element tag names. We can change the custom element tag names by overriding it to control using getComponentNameForNode.

ko.components.getComponentNameForNode = function(node) {
   ...
   ...   //function code
   ...
}

Registering Custom Elements

The custom elements can be made available immediately, if the default component loader is used and hence the component is registered using ko.components.register. If we are not using the ko.components.register and implementing the custom component loader, then the custom element can be used by defining any element name of choice. There is no need to specify configuration when you are using ko.components.register as the custom component loader does not use it anymore.

ko.components.register('custom-element', { ......... });

Example

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Components</title>
      <script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
      <script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
   </head>
   
   <body>
      <!--params attribute is used to pass the parameter to component viewModel.-->
      <click params = "a: a, b: b"></click>

      <!--template is used for a component by specifying its ID -->
      <template id = "click-l">
         <div data-bind = "text: a"></div>

         <!--Use data-bind attribute to bind click:function() to ViewModel. -->
         <button data-bind = "click:function(){callback(1)}">Increase</button>
         <button data-bind = "click:function(){callback(-1)}">Decrease</button>
      </template>

      <script>
         //Here components are registered
         ko.components.register('click', {
            
            viewModel: function(params) {
               self = this;
               this.a = params.a;
               this.b = params.b;

               this.callback = function(num) {
                  self.b(parseInt(num));
                  self.a( self.a() + parseInt(num) );
               };
            },
            template: { element: 'click-l' }
         });

         //keeps an eye on variable for any modification in data
         function viewModel() {
            this.a = ko.observable(2);
            this.b = ko.observable(0);
         }

         ko.applyBindings(new viewModel() );
      </script>
      
   </body>
</html>

Output

Let's carry out the following steps to see how the above code works −

  • Save the above code in component_register.htm file.

  • Open this HTML file in a browser.

Component Loaders

Component loaders are used to pass the template/viewModel pair asynchronously for the given component name.

The default component loader

The default component loader depends on the explicitly registered configuration. Each component is registered before using the component.

ko.components.defaultLoader

Component Loader Utility Functions

The default component loader can read and write using the following functions.

Sr.No. Utility functions & Description
1

ko.components.register(name, configuration)

Component is registered.

2

ko.components.isRegistered(name)

If the particular component name is already registered, then it returns as true else false.

3

ko.components.unregister(name)

The component name is removed from the registry.

4

ko.components.get(name, callback)

This function goes turn by turn to each registered loader to find who has passed the viewModel/template definition for component name as first. Then it returns viewModel/template declaration by invoking callback. If the registered loader could not find anything about the component, then it invokes callback(null).

5

ko.components.clearCachedDefinition(name)

This function can be called when we want to clear the given component cache entry. If the component is needed next time, again the loaders will be consulted.

Implementing a custom component loader

The custom component loader can be implemented in the following ways −

  • getConfig(name, callback) − Depending on the names, we can pass configurations programatically. We can call callback(componentConfig) to pass the configurations, where the object componentConfig can be used by the loadComponent or any other loader.

  • loadComponent(name, componentConfig, callback) − This function resolves the viewModel and the template portion of config depending upon the way it is configured. We can call callback(result) to pass the viewmodel/template pair, where the object result is defined by the following properties.

    • template − Required. Return array of DOM nodes.

    • createViewModel(params, componentInfo) − Optional. Returns the viewModel Object depending on how the viewModel property was configured.

  • loadTemplate(name, templateConfig, callback) − DOM nodes is passed in a template using custom logic. The object templateConfig is a property of the template from an object componentConfig. callback(domNodeArray) is called to pass an array of DOM nodes.

  • loadViewModel(name, templateConfig, callback) − viewModel factory is passed in a viewModel configuration using custom logic.

Advertisements