Dynamiczne zakładki z kliknięciem przez użytkownika wybranych komponentów
Próbuję skonfigurować system tab, który pozwala komponentom na zarejestrowanie się (z tytułem). Pierwsza karta jest jak skrzynka odbiorcza, istnieje wiele działań/elementów linków do wyboru dla użytkowników, a każde z tych kliknięć powinno być w stanie utworzyć instancję nowego komponentu, po kliknięciu. Akcje / linki pochodzą z JSON.
Instancyjny komponent zarejestruje się jako nowa karta.
Nie jestem pewien, czy to jest najlepsze podejście? Do tej pory jedynym poradniki, które widziałem, są dla kart statycznych, co nie pomaga.Jak na razie mam tylko usługę tabs, która jest bootstrapped w main, aby utrzymywać się w całej aplikacji. Wygląda to mniej więcej tak:
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);
}
}
Pytania:
- Jak mogę mieć dynamiczną listę w skrzynce odbiorczej, która tworzy nowe (różne) karty? Zgaduję, że
DynamicComponentBuilder
zostanie użyty? - W Jaki Sposób komponenty mogą być tworzone ze skrzynki odbiorczej (po kliknięciu) rejestrują się jako zakładki i mogą być wyświetlane? I ' m zgaduję
ng-content
, ale nie mogę znaleźć zbyt wiele informacji na temat tego, jak z niego korzystać
EDIT: próba wyjaśnienia.
Pomyśl o skrzynce odbiorczej jak o skrzynce odbiorczej. Elementy są pobierane jako JSON i wyświetla kilka elementów. Po kliknięciu jednego z elementów zostanie utworzona nowa karta z akcją "Typ". Typ jest wtedy składnikiem.Edytuj 2: Obraz .
3 answers
Update
Kątowa 5 StackBlitz przykład
Update
ngComponentOutlet
został dodany do 4.0.0-beta.3
Update
Istnieje NgComponentOutlet
praca w toku, która robi coś podobnego https://github.com/angular/angular/pull/11235
RC.7
Plunker przykład 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();
}
}
}
Przykład użycia
// 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 {}
Zobacz angular.io DYNAMIC COMPONENT LOADER
Starsze wersje
To zmieniło się ponownie w Angular2 RC.5
Zaktualizuję poniższy przykład, ale jest to ostatni dzień przed wakacjami.
Ten przykład Plunkera pokazuje jak dynamicznie tworzyć komponenty w RC.5
Update-use ViewContainerRef .createComponent()
Ponieważ DynamicComponentLoader
jest przestarzałe, podejście musi zostać zaktualizowane ponownie.
@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 przykład RC.4
Plunker przykład beta.17
Update-use 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 przykład beta.17
Oryginalny
Nie jestem do końca pewien z twojego pytania, Jakie są Twoje wymagania, ale myślę, że powinno to zrobić to, co chcesz.
Komponent Tabs
otrzymuje tablicę typy przekazywane i tworzy "tabulatory"dla każdego elementu w tablicy.
@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 przykład beta.15 (nie na podstawie Twojego Plunkera)
Istnieje również sposób przekazywania danych, które mogą być przekazywane do dynamicznie tworzonego komponentu jak (someData
musiałyby być przekazywane jak type
)
this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
cmpRef.instance.someProperty = someData;
this.cmpRef = cmpRef;
});
Istnieje również wsparcie dla korzystania z iniekcji zależności z usługami współdzielonymi.
Po Więcej szczegółów zobacz https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-26 07:16:52
Nie jestem wystarczająco fajny na komentarze. Naprawiłem plunker z zaakceptowanej odpowiedzi do pracy dla rc2. Nic wymyślnego, linki do CDN były po prostu zepsute to wszystko.
'@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'
},
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 20:52:12
Istnieje komponent gotowy do użycia (kompatybilny z rc5)
ng2-kroki
który używa Compiler
do wstrzykiwania komponentu do kontenera step
i serwis do okablowania wszystkiego razem (synchronizacja danych)
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);
});
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-10 15:58:54