242 votes

Deux onglets dynamiques angulaires avec un clic sur les composants choisis

Je suis en train de configurer un système d'onglets qui permet de composants à s'inscrire eux-mêmes (avec un titre). Le premier onglet est comme une boîte de réception, il y a beaucoup d'actions/lien articles à choisir pour les utilisateurs, et chacun de ces clics devrait être en mesure d'instancier un nouvel élément, cliquez sur. Les actions / liens vient de JSON.

Le instancié composant va alors s'inscrire en tant que nouvel onglet.

Je ne sais pas si c'est la meilleure approche? Jusqu'à présent les seuls guides que j'ai vu sont les onglets, ce qui n'aide pas.

Jusqu'à présent, je n'ai que les onglets de service qui est amorcé dans les principales persister tout au long de l'application, semble ~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:

1) Comment puis-je avoir une liste dynamique de la boîte de réception qui crée de nouvelles (différents) onglets? Je suis sorte de deviner le DynamicComponentBuilder serait utilisé?

2) Comment 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-contenu mais je ne trouve pas beaucoup d'info sur la façon de l'utiliser

Edit: Essayer de clarifier

Pensez à la boîte de réception de courrier de la boîte de réception, les éléments sont récupérés sous forme de JSON et affiche plusieurs éléments. Une fois que l'un des éléments est cliqué, un nouvel onglet est créé avec des éléments d'action "type". Le type est alors un composant

Edit2: Image

http://i.imgur.com/yzfMOXJ.png

281voto

Günter Zöchbauer Points 21340

Ce nouveau changé dans Angular2 RC.5

Je mettrai à jour l'exemple ci-dessous, mais c'est le dernier jour avant les vacances.

Cette Plunker exemple montre comment créer dynamiquement des composants RC.5

Mise à jour - ViewContainerRef.createComponent()

Parce qu' DynamicComponentLoader est obsolète, l'approche doit être mise à jour.

@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 bêta.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();
    }    
  }
}

Plunker exemple bêta.17

d'origine

Pas tout à fait sûr de votre question, de vos besoins mais je pense que cela devrait faire ce que vous voulez.

L' Tabs composant reçoit un tableau de types passé et elle crée des "onglets" pour chaque élément dans le 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 Plunker)

Il est aussi un moyen de transmettre des données qui peuvent être transmises à l'créés dynamiquement composant (someData devra être transmis comme type)

    this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
  cmpRef.instance.someProperty = someData;
  this.cmpRef = cmpRef;
});

Il y a aussi un certain soutien pour utiliser l'injection de dépendance avec les services partagés.

Pour plus de détails, voir https://angular.io/docs/js/latest/api/core/DynamicComponentLoader-class.html

22voto

davimusprime Points 52

Je ne suis pas assez cool pour les commentaires. J'ai corrigé le plunker de la réponse acceptée au travail pour rc2. Rien d'extraordinaire, les liens vers le CDN étaient simplement cassé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'
},
 

https://plnkr.co/edit/kVJvI1vkzrLZJeRFsZuv?p=preview

17voto

neuronet Points 311

il y a un composant prêt à l'emploi (compatible rc5) ng2-steps qui utilise Compiler pour injecter le composant dans le conteneur et le service pour tout connecter (synchronisation des données)

     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);
    });
  }

}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X