Nesting Components
In component-based frameworks like Angular, React we create a root component and build a tree. With ngBackbone we can do the same. Let's create a new child component foo.ts
:
import { Component, View } from "ng-backbone";
@Component({
el: "ng-foo",
template: `Hello, it's Foo`
})
export class FooView extends View {
initialize(){
this.render();
}
}
Then we add its sibling bar.ts
import { Component, View } from "ng-backbone";
@Component({
el: "ng-bar",
template: `Hello, it's Bar`
})
export class BarView extends View {
initialize(){
this.render();
}
}
As you can see from the code both of the components render themselves during initialization. In the root app.ts
we can use views
map to refer to the nesting components:
import { Component, View } from "ng-backbone";
import { FooView } from "./foo";
import { BarView } from "./bar";
@Component({
el: "ng-hello",
views: {
foo: FooView,
bar: BarView
},
template: `<ng-foo></ng-foo> <ng-bar></ng-bar>`
})
class AppView extends View {
}
let app = new AppView();
app.render();
Here we specified the locations for the child components in the template by placing there elements ng-foo
and ng-bar
. These are the elements we have bound our nested components to.
After compiling app.ts
we get in the browser both the nesting components rendered within the root:
Child Component and Construction Options
Well, what if we need to pass specific constructor arguments to a nesting component? Not a problem. We can specify a subordinated component as an array where the constructor goes in the first element and view options in the second:
@Component({
el: "ng-hello",
views: {
foo: [ FooView, { name: "foo" } ],
bar: [ BarView, { name: "bar" } ]
},
template: `<ng-foo></ng-foo> <ng-bar></ng-bar>`
})
Child components get constructed after the parent first rendering. Thus they can bind to the elements of the parent template. Besides construction ngBackbone also takes care about destruction. Whenever
remove()
method is called on the parent, this method is being invoked per each child recursively.
Accessing Sub-components
Within parent component child ones are available in views
map. You can access the instance of a sub-component as this.views.get( "name" )
. Thus we obtain the control over child component from the parent:
@Component({
el: "ng-hello",
events: {
"click [data-bind=toggleFoo]" : "toggleFoo",
"click [data-bind=toggleBar]" : "toggleBar"
},
views: {
foo: FooView,
bar: BarView
},
template: `
<button data-bind="toggleFoo">Show Foo</button>
<button data-bind="toggleBar">Show Bar</button>
<ng-foo></ng-foo> <ng-bar></ng-bar>`
})
class AppView extends View {
toggleFoo(){
this.views.get( "foo" ).toogle();
}
toggleBar(){
this.views.get( "bar" ).toogle();
}
}
Communicating between Parent and Child
As we just examined the parent component has the links to child ones. But every child component also has a link to the parent (this.parent
). We can use it to implement bi-directional communication.
Let's write a child component that has a bound echo
model. The template renders echo.msg
. When the component initializes it changes echo.msg
of it's parent, assuming the parent has similar model:
child.ts
import { Component, View, Model } from "ng-backbone";
@Component({
el: "ng-child",
models: {
echo: new Model({
msg: ""
})
},
template: `<h2>It's Child</h2>
<p>Echo: <output data-ng-text="echo.msg"></output></p>
`
})
export class ChildView extends View {
initialize(){
this.render();
this.parent.models.get( "echo" ).set( "msg", "Hello, it's Child" );
}
}
Now we create the parent component. During initialization it also changes the model of the child.
parent.ts
import { Component, View, Model } from "ng-backbone";
import { ChildView } from "./child";
@Component({
el: "ng-parent",
views: {
child: ChildView
},
models: {
echo: new Model({
msg: ""
})
},
template: `<h2>It's Parent</h2>
<p>Echo: <output data-ng-text="echo.msg"></output></p>
<ng-child></ng-child>
`
})
export class ParentView extends View {
initialize(){
this.render();
let echo = this.views.get( "child" ).models.get( "echo" );
echo.set( "msg", "Hello, it's Parent" );
}
}
new ParentView();
When we compile the code and run it a browser, we see that parent received and displayed the message from child and vice versa.
Maintaining bindings on parent view change
Let's say we have an imaginary task. We have to create a list where every item has own view. So as for items the example view may look like:
@Component({
el: "ng-item",
template: "it's item"
})
class ItemView extends View {
initialize(){
this.render();
}
}
The parent (list) view has a bound collection named items
and a subview named also items
. As you remember we expect subview binding to ng-item
element.
let items = new Collection([ new Model() ]);
@Component({
tagName: "ng-list",
template: "<ng-item data-ng-for=\"let item of items\"></ng-item>",
collections: {
items: items
},
views: {
items: ItemView
}
})
class ListView extends View {
initialize(){
this.render();
}
}
Initially the collection has just a single element and on render we have <ng-list><ng-item>it's item</ng-item></ng-list>
let list = new ListView();
list.views.getAll( "foo" ).length; // 1
list.views.get( "foo" ); // ItemView
With method list.views.getAll( "foo" )
we can access the array of bound ItemView instances. Here it consists of one element. However let's see what happens if we change the collection:
items.add([ new Model() ]);
view.on( "component-did-update", () => {
list.views.getAll( "foo" ).length; // 2
list.views.get( "foo", 0 ); // ItemView
list.views.get( "foo", 1 ); // ItemView
done();
});
The method list.views.getAll( "foo" )
indicates that we have now 2 subview instances matching ng-item
element. We can access a particular instance like list.views.get( "foo", index )
Last updated
Was this helpful?