I've been learning ractive.js for few weeks and I am trying to build a feature within a larger webpage that lists out three items. Each list has it's own title and description. Each list is a component (the list-component) with its own styles and javascript (each component is a separate html). I also have a component (description-component) that is responsible for writing out the title and description and it is in a separate html file. The problem I am having is 'importing' or bringing in that description-component into the list-component so the output in the main index html file is this:
<description title="Name">
<list-items/>
</description>
So far I looked into the yield directive and tried some examples but that was a simple example using two components on the same document. I am not sure if that is the correct way. This is what is in the description-component html file:
<p>{{title}}</p>
I tried using ractive-load.js and load up the description-component html for every list-component file like in this example on their github:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
var foo = new components.Foo({
el: 'body',
data: { ... }
});
var bar = new components.Bar({
el: 'body',
data: { ... }
});
}).catch( handleError );
and that seemed like an overkill and thought there must be a better way. How would I go about approaching this?
The loading of Ractive components is not directly related to handling nested components and passing yielded content to components.
Ractive.load
works by fetching one or more html files that it then resolves to Components. From your code samples, it seems you've got that part working. For larger apps, I usually use a build tool plugin to pre-bundle all my Ractive components so they're deliverable in one file or even rolled into my main bundle js.
However the components are made available, they need to be registered on either the consuming component (or a view parent) or globally:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
Ractive.components.foo = components.Foo;
Ractive.components.bar = components.Bar;
// now create your actual top-level view instance(s)
}).catch( handleError );
In component architectures, you create trees or bushes of components. I usually only have one top level app
component, but it certainly is feasible to create multiple trees that start at different places in the DOM.
For simplicity, continuing on the above example, let's create a generic ractive instance that uses the two components Foo and Bar we registered (notice we use the property name we assigned to Ractive.components
):
const ractive = new Ractive({
el: document.body,
sayHello() {
alert('hello from main Ractive instance');
},
template: `
<h1>my kewl app</h1>
<foo>
<h3 on-click="sayHello()">hello world</h3>
<bar bizz="{{buzz}}"></bar>
</foo>
`
});
In this case we're passing some content (html and our bar component) to the foo component by including it as the <foo>
element content.
How this content is used depends on the foo
template. There are two choices:
<div>
<h2>foo component template</h2>
{{>content}}
<p>some more stuff</p>
</div>
In this example, we're using the built-in partial "content" to tell the template to put provided content in the {{>content}}
slot. In this case the provided content is passed like a partial, and any directives will be applied against the foo
component. So in this example, clicking on the h3 header will try and run foo.sayHello()
. And when passing the bizz
data to the bar
component, Ractive will start looking in the foo
component for buzz
.
Often, this isn't what you want. What you would rather have happen is for the parent to own the directives. So instead the foo
template would look like:
<div>
<h2>foo component template</h2>
{{yield}}
<p>some more stuff</p>
</div>
And now when h3
is clicked, it calls the main ractive.sayHello()
method as the content was passed to be rendered in the DOM by the the foo component, but it was still owned by the passing instance. Likewise Ractive will start look for buzz
in the main instance, not foo
.
With yield
you can also name multiple partials to be passed:
<!-- "foo" template: -->
<div>
<header>{{yield header}}</header>
<section>
<div>something here</div>
<div>{{yield message}}</div>
</section>
</div>
<!-- using "foo": -->
<div>
<foo>
{{#partial header}}
<h2>This is the header to use</h2>
{{/partial}}
{{#partial message}}
<p>This is the message to use, with a bar component to boot</p>
<bar></bar>
{{/partial}}
</foo>
</div>