Components
Components
Now that you've learned about observables, templates, and controls, it's time to learn about can.Component. can.Component makes it easy to combine the functionality of these features. We'll use it to rewrite the todo example.
Create a component constructor function by extend can.Component like:
Where:
Now let's dive into component's properties a bit more.
Tag
A component represents a custom html element whose nodeName is specified by the component's tag attribute. To create a can.Component constructor function that manages functionality on a
<todos-editor>elements, extend can.Component like:Now, when a
<todos-editor>element is found in a mustache template, an instance of the component is created on the element.This control doesn't do anything. Lets change that by filing out other properties.
Template
Lets put an
<input/>element inside<todos-editor>elements by adding a template property:This replaces any source content with the component's template. The result of rendering
templatelooks like:Notice that "todos-editor element" is removed.
To render the source content within the template, add a
<content/>tag like:This results in:
If no source content is provided between the custom tags, you can specify default content to use within the
<content></content>tags like:If the source template is changed to:
This results in:
You can also specify the template as a can.view.renderer like:
By default the component's template renders with the same [can.view.Scope scope] as the scope where the custom element is found within the source template. But, you can adjust the scope with can.Component's scope property.
Scope
A template's scope property allows you to adjust the scope used to render the component's template. If a plain JavaScript object is used, that object is used to extend and create an instance of can.Map and add to the top to the scope used to render the template.
For example, we can add a visible property to control if the input element is visible or not:
Scope bindings
This isn't interesting without a way to change toggling the visible property. We can tell our template to call a
togglemethod on the scope anytime someone clicks the form with template bindings like:Check it out here:
When bindings are used like this, the scope function is called back with the element's context, the element, and the event.
Scope value functions
Scope functions can also be called for their value. For example:
Scope as a can.Map constructor function
The scope object can also be defined as a can.Map constructor function. This makes it easier to test the scope object independent of the component's rendering. For example:
Passing values to a component's scope
Often, you want to pass values to a component. This is done by setting attributes on the component's element. For example, we might want to pass a todo to the todo editor from the source template. To do this, add a
todo='mytodo'attribute.Notice the
can-valueattribute on the input element. This sets up a two-way binding between the todo's name and the input element. This lets you change the todo's name.Sometimes, you want to specify attribute values that are not looked up in the scope. For example, you might want to give
todos-editorplaceholder text as follows:We can modify the component to read the string placeholder value by setting
placeholderin the scope to "@". This is a special flag that indicates to simply use the attribute's value.If you remove the input's text, a placeholder will show up:
Helpers
The helpers object registers local helpers avaialble within the component. The following lists todo and adds a
todoClasshelper that is used to set the className on a todo's<li>element:Notice that
options.contextis used to retrieve the todo becausethiswithintodoClassis the scope.Events
A component's events object is used to create a [can.Control] that has access to scope as
this.scope. Use it to listen to events safely.For example, we can create a
todos-appcomponent that manages the high-level state of the application. It listens to changes in can/route and updates the scope'stodoproperty. And, if a todo is destroyed that matches the route'sid, the route'sidis removed:You can use templated event binding to listen to changes in scope objects. Adding the following to
todo-app's events object listens to todo changes and saves the changes.Finally, to put this all together, we render a template like: