angular2-component-outlet
angular2-component-outlet copied to clipboard
[Suggestion] Add ngInclude functionallity
@laco0416 I'm actually using my own template system in my ng2 apps, it's something like ngInclude from AngularJS. I can declare a template and use it in others places. For example:
<!-- `templateId` directive register the template in a internal container -->
<template templateId="rowTmpl" let-data>
{{data.name}} {{data.surname}} from {{data.company}}
</template>
<template templateId="detailTmpl" let-data let-last="last">
Name: {{data.name}} <br/>
Surname: {{data.surname}} <br/>
Company: {{data.company}} <br/>
Age: {{data.age}} <br/>
Address: {{data.address}} <br *ngIf="last"/>
</template>
<ul>
<li *ngFor="let item of persons; let index = index; let last = last; let first = first" (click)="toggleSelected(item)">
<div *ngIf="item.selected">
<!-- `includeTemplate` is a directive like `ngTemplateOutlet` with extra features -->
<div *includeTemplate="'detailTmpl', context: item, extraContext: { index: index, last: last, first: first}"></div>
</div>
<div *ngIf="!item.selected">
<div *includeTemplate="'rowTmpl, context: item"></div>
</div>
</li>
</ul>
includeTemplate
accepts a context
object that is used as $implicit
context and extraContent
that can be used with let-localvar="extraContextField"
The example shows a list of persons with a template if you click on a person it shows the data with another template.
Nowadays you could do it using template ref variables, but the scope of template ref variables is limited, so I use an internal template container.
So far, the new functionallity. But we could go further. We could to have an html file with several templates:
<!-- templates.html -->
<template templateId="tmpl1">...</template>
<template templateId="tmpl2">...</template>
<template templateId="tmpl3">...</template>
Then we could load this html file into a component using angular2-component-outlet
:
// AppComponent.ts
const templates = require("./templates.html");
@Component({
selector: "app",
templateUrl: "AppComponent.html"
})
export class AppComponent {
templatesHtml = templates;
...
...
}
<!-- AppComponent.html -->
<div *componentOutlet="templatesHtml"></div>
<div includeTemplate="tmpl2"></div>
In addition, we could modify conponent-outlet
to accept a templateId
and extract the template with this templatId
from the html and only render the desired template, doing something like:
<!-- AppComponent.html -->
<div *componentOutlet="templatesHtml; templateId: 'tmpl2'"></div>
<div includeTemplate="tmpl2"></div>
I have implemented this and I'm using it in my apps. I'm thinking about public this on github but I not have much time to support people, and I'm thinking to include this to angular2-component-outlet
repository.
If you agree, I can do a pull request with the implementation and I can write the documentation too, perhaps I need help with the tests.
Another approach would be to create other repository, something like "ng2Include" with all the features included ' component-outlet'.
What do you think about?
We could call it ng2-dynamic
or ... I don't know! :D
@tolemac Sorry for late reply. In summary, you think ComponentOutlet
can support TemplateRef
instead of a string template, right? On that point, I agree with your proposal.
I expect...
// string template style
<div *componentOutlet="template: '...'"></div>
// TemplateRef style
<template #myTmp>
</template>
<div *componentOutlet="templateRef: myTmp"></div>
Yes @laco0416, that would be the begining. But template reference variables has a limited scope:
Looks this:
<div *ngIf="objectType=='human'" id="IfDiv">
<template #rowTemplate let-obj>
<span>{{obj.name}} </span>
<span>{{obj.surname}} </span>
</template>
</div>
<div *ngIf="objectType=='dog'">
<template #rowTemplate let-obj>
<span>{{obj.name}} </span>
<span>{{obj.dogBreed}} </span>
</template>
</div>
<div *componentOutlet="templateRef: rowTemplate"></div>
#rowTemplate
template is not available outside IfDiv
div. And you can have problems using the same ref-var several times. From docs:
Do not define the same variable name more than once in the same template. The runtime value will be unpredictable.
Then you need to build another way to obtain the templates and accept both, templateRef
and string
.
By the other hand, IMHO, I don't like to mix dynamic component loading and template include on the same component, the implementation of each thing is very different and resulted in ugly code, I have had two things in the same component and it was auful. I think when a component have two behavior so differents is better to have two components.
@tolemac I think your example code can be rewritten as below:
<template #humanTemplate let-obj>
<span>{{obj.name}} </span>
<span>{{obj.surname}} </span>
</template>
<template #dogTemplate let-obj>
<span>{{obj.name}} </span>
<span>{{obj.dogBreed}} </span>
</template>
<div *ngIf="objectType=='human'">
<div *componentOutlet="templateRef: humanTemplate"></div>
</div>
<div *ngIf="objectType=='dog'">
<div *componentOutlet="templateRef: dogTemplate"></div>
</div>
I think, in this case, simply ComponentOutlet can use TemplateRef
. Am I wrong?
Yes @laco0416, you have solved this example, ok. But you can do it using NgTemplateOutlet.
Now suppose you have templates in a separate .html file.
<!-- templates.html -->
<template #tmpl1>...</template>
<template #tmpl2>...</template>
<template #tmpl3>...</template>
How to use them?
@tolemac Let’s think about these separately. I agree on that ComponentOutlet should be able to take TemplateRef
but I don't think it should care about how to pass the TemplateRef (at first step).
If you have something pipe to transform templateId
to TemplateRef
, maybe it can work with componentOutlet.
<div *componentOutlet="templateRef: ('tmpl2' | templateById)"></div>
how do you think about the above?
Yes @laco0416, you could. Then, would you like to mix two features on the same component?
If yes, we have to consider the sense of each options.
When you use dynamic component loading
you can use:
<div *componentOutlet="template; context: self; selector:'my-component'"></div>
There are three options:
-
template
: html to load. -
context
: object context inner the component. -
selector
: html tag for component.
When you use TemplateRef
, the selector
option has no sense, neither template
. And when you use TemplateRef
included through ViewContainer.createEmbeddedView
you can set a context and you can use an $implicit
context to be use with let-
.
Then, I see two very differents uses of componentOutlet
component where neither option is common.
Neither option is common and the implementation of two usages are very very different. It is for this reason that I do not like to be together.
Repeating me, I think when a component have two behavior so differents is better to have two components.