Onglets dynamiques angulaires avec les composants choisis par l'utilisateur-click
j'essaie de configurer un système d'onglets qui permet aux composants de s'enregistrer (avec un titre). Le premier onglet est comme une boîte de réception, Il ya beaucoup d'actions / éléments de lien à choisir pour les utilisateurs, et chacun de ces clics devrait être en mesure d'instancier un nouveau composant, sur le clic. Les actions / liens proviennent de JSON.
le composant instancié s'enregistrera alors comme un nouvel onglet.
Je ne suis pas sûr que ce soit le "meilleur" approche? Sofar les seuls guides que j'ai vu sont pour les onglets statiques, ce qui n'aide pas.
Jusqu'à présent, je n'ai que le service tabs qui est bootstrapped dans la main pour persister tout au long de l'application, ressemble à quelque chose comme ça.
export interface ITab { title: string; }
@Injectable()
export class TabsService {
private tabs = new Set<ITab>();
addTab(title: string): ITab {
let tab: ITab = { title };
this.tabs.add(tab);
return tab;
}
removeTab(tab: ITab) {
this.tabs.delete(tab);
}
}
Questions:
- Comment puis-je avoir une liste dynamique dans la boîte de réception qui crée de nouveaux onglets (différents)? Je pense que le
DynamicComponentBuilder
serait utilisé? - How peut les composants créés à partir de la boîte de réception (clic) s'inscrire eux-mêmes comme des onglets et aussi être montré? Je devine
ng-content
, mais je ne peux pas trouver beaucoup d'informations sur la façon de l'utiliser
Edit: Essayer de clarifier
pensez à la boîte de réception comme une boîte aux lettres, les éléments sont récupérés comme JSON et affiche plusieurs éléments. Une fois qu'un des éléments est cliqué, un nouvel onglet est créé avec l'action 'type'des éléments. Le type est alors un composant
Modifier 2: Image
http://i.imgur.com/yzfMOXJ.png
3 réponses
RC.7
// Helper component to add dynamic components
@Component({
selector: 'dcl-wrapper',
template: `<div #target></div>`
})
export class DclWrapper {
@ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef;
@Input() type: Type<Component>;
cmpRef: ComponentRef<Component>;
private isViewInitialized:boolean = false;
constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler) {}
updateComponent() {
if(!this.isViewInitialized) {
return;
}
if(this.cmpRef) {
// when the `type` input changes we destroy a previously
// created component before creating the new one
this.cmpRef.destroy();
}
let factory = this.componentFactoryResolver.resolveComponentFactory(this.type);
this.cmpRef = this.target.createComponent(factory)
// to access the created instance use
// this.compRef.instance.someProperty = 'someValue';
// this.compRef.instance.someOutput.subscribe(val => doSomething());
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if(this.cmpRef) {
this.cmpRef.destroy();
}
}
}
exemple d'utilisation
// Use dcl-wrapper component
@Component({
selector: 'my-tabs',
template: `
<h2>Tabs</h2>
<div *ngFor="let tab of tabs">
<dcl-wrapper [type]="tab"></dcl-wrapper>
</div>
`
})
export class Tabs {
@Input() tabs;
}
@Component({
selector: 'my-app',
template: `
<h2>Hello {{name}}</h2>
<my-tabs [tabs]="types"></my-tabs>
`
})
export class App {
// The list of components to create tabs from
types = [C3, C1, C2, C3, C3, C1, C1];
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App, DclWrapper, Tabs, C1, C2, C3],
entryComponents: [C1, C2, C3],
bootstrap: [ App ]
})
export class AppModule {}
Voir aussi angulaire.IO DYNAMIC COMPONENT LOADER
versions plus anciennes xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
cela a changé à nouveau dans Angular2 RC.5
je vais mettre à jour l'exemple ci-dessous, mais c'est le dernier jour avant les vacances.
ce exemple de plongeur montre comment créer dynamiquement des composants dans RC.5
mise à Jour - ViewContainerRef .createComponent()
parce que DynamicComponentLoader
est déprécié, l'approche doit être mise à jour à nouveau.
@Component({
selector: 'dcl-wrapper',
template: `<div #target></div>`
})
export class DclWrapper {
@ViewChild('target', {read: ViewContainerRef}) target;
@Input() type;
cmpRef:ComponentRef;
private isViewInitialized:boolean = false;
constructor(private resolver: ComponentResolver) {}
updateComponent() {
if(!this.isViewInitialized) {
return;
}
if(this.cmpRef) {
this.cmpRef.destroy();
}
this.resolver.resolveComponent(this.type).then((factory:ComponentFactory<any>) => {
this.cmpRef = this.target.createComponent(factory)
// to access the created instance use
// this.compRef.instance.someProperty = 'someValue';
// this.compRef.instance.someOutput.subscribe(val => doSomething());
});
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if(this.cmpRef) {
this.cmpRef.destroy();
}
}
}
Plunker exemple RC.4
Plunker exemple beta.17
mise à Jour - loadNextToLocation
export class DclWrapper {
@ViewChild('target', {read: ViewContainerRef}) target;
@Input() type;
cmpRef:ComponentRef;
private isViewInitialized:boolean = false;
constructor(private dcl:DynamicComponentLoader) {}
updateComponent() {
// should be executed every time `type` changes but not before `ngAfterViewInit()` was called
// to have `target` initialized
if(!this.isViewInitialized) {
return;
}
if(this.cmpRef) {
this.cmpRef.destroy();
}
this.dcl.loadNextToLocation(this.type, this.target).then((cmpRef) => {
this.cmpRef = cmpRef;
});
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if(this.cmpRef) {
this.cmpRef.destroy();
}
}
}
original
pas tout à fait sûr de votre question Quelles sont vos exigences, mais je pense que cela devrait faire ce que vous voulez.
le composant Tabs
reçoit un tableau de types passés et crée des "onglets" pour chaque élément du tableau.
@Component({
selector: 'dcl-wrapper',
template: `<div #target></div>`
})
export class DclWrapper {
constructor(private elRef:ElementRef, private dcl:DynamicComponentLoader) {}
@Input() type;
ngOnChanges() {
if(this.cmpRef) {
this.cmpRef.dispose();
}
this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
this.cmpRef = cmpRef;
});
}
}
@Component({
selector: 'c1',
template: `<h2>c1</h2>`
})
export class C1 {
}
@Component({
selector: 'c2',
template: `<h2>c2</h2>`
})
export class C2 {
}
@Component({
selector: 'c3',
template: `<h2>c3</h2>`
})
export class C3 {
}
@Component({
selector: 'my-tabs',
directives: [DclWrapper],
template: `
<h2>Tabs</h2>
<div *ngFor="let tab of tabs">
<dcl-wrapper [type]="tab"></dcl-wrapper>
</div>
`
})
export class Tabs {
@Input() tabs;
}
@Component({
selector: 'my-app',
directives: [Tabs]
template: `
<h2>Hello {{name}}</h2>
<my-tabs [tabs]="types"></my-tabs>
`
})
export class App {
types = [C3, C1, C2, C3, C3, C1, C1];
}
Plunker exemple bêta.15 (non basé sur votre Piston)
il y a aussi un moyen de transmettre des données qui peuvent être passées au composant créé dynamiquement comme ( someData
devrait être passé comme type
)
this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
cmpRef.instance.someProperty = someData;
this.cmpRef = cmpRef;
});
il existe également un certain soutien pour utiliser l'injection de dépendances avec des services partagés.
pour plus de détails, voir https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
Je ne suis pas assez cool pour les commentaires. J'ai fixé le plongeur de la réponse acceptée pour travailler pour rc2. Rien de fantaisiste, les liens avec le CDN ont été brisés, c'est tout.
'@angular/core': {
main: 'bundles/core.umd.js',
defaultExtension: 'js'
},
'@angular/compiler': {
main: 'bundles/compiler.umd.js',
defaultExtension: 'js'
},
'@angular/common': {
main: 'bundles/common.umd.js',
defaultExtension: 'js'
},
'@angular/platform-browser-dynamic': {
main: 'bundles/platform-browser-dynamic.umd.js',
defaultExtension: 'js'
},
'@angular/platform-browser': {
main: 'bundles/platform-browser.umd.js',
defaultExtension: 'js'
},
il y a un composant prêt à l'emploi (compatible rc5))
ng2-étapes
qui utilise Compiler
pour injecter un composant dans un récipient step
et le service pour le câblage tout ensemble (Data sync)
import { Directive , Input, OnInit, Compiler , ViewContainerRef } from '@angular/core';
import { StepsService } from './ng2-steps';
@Directive({
selector:'[ng2-step]'
})
export class StepDirective implements OnInit{
@Input('content') content:any;
@Input('index') index:string;
public instance;
constructor(
private compiler:Compiler,
private viewContainerRef:ViewContainerRef,
private sds:StepsService
){}
ngOnInit(){
//Magic!
this.compiler.compileComponentAsync(this.content).then((cmpFactory)=>{
const injector = this.viewContainerRef.injector;
this.viewContainerRef.createComponent(cmpFactory, 0, injector);
});
}
}