Передача компонента в качестве "аргумента" другому компоненту в Angular 2
Я новичок в веб-разработке, и я только начал создавать приложение Angular 2. На данный момент я пытаюсь создать некоторые компоненты/формы CRUD, но я нахожу, что сам дублирую много кода. Я не буду спрашивать, какие общие рекомендации избегать дублирования кода и обеспечить повторное использование компонентов при разработке приложений CRUD с помощью Angular2, потому что сообщение будет заблокировано. Я предпочту сосредоточиться на одном конкретном аспекте:
У меня есть страница "CRUD", в которой есть список (это фактически таблица html) ресурсов и несколько кнопок, которые открывают формы "создавать", "читать" и "редактировать". Этот список является отдельным компонентом самостоятельно и отдельными компонентами create/read/edit. Каждая строка таблицы содержит другой компонент, который знает, как визуализировать элемент ресурса. Я назову этот компонент <resource-item>
. Тем не менее, у меня есть несколько таких "страниц CRUD", каждая страница для другого ресурса. Поэтому я хочу повторно использовать компонент списка для всех ресурсов. Поэтому первое, что нужно сделать, - добавить в компонент списка входы или атрибуты, чтобы управлять своими метками. Все идет нормально.
Но как насчет компонента <resource-item>
? Каждый ресурс моего приложения может иметь совершенно другую структуру. В результате мне понадобятся разные компоненты для разных ресурсов, например: <resource-a-item>
, <resource-b-item>
и т.д. Как я могу указать, какой компонент элемента ресурса я хочу использовать каждый раз при создании компонента списка?
Спасибо за ваше время.
Ответы
Ответ 1
Это, по-видимому, идеально подходит для пересылки контента.
Angular 2 поставляется с компонентом ng-content, который позволяет вставлять внешние html/компоненты в качестве содержимого вашего компонента.
Вам просто нужно использовать в том месте, где вы хотите, чтобы содержимое отображалось в вашем компоненте.
Например:
import {Component} from 'angular2/core'
@Component({
selector: 'holder',
providers: [],
template: `
<div>
<h2> Here the content I got </h2>
<ng-content></ng-content>
</div>
`,
directives: []
})
export class Holder {
constructor() {
}
}
И вы можете указать содержимое, которое вы хотите ввести из родительского элемента таким образом:
import {Component} from 'angular2/core';
import {Holder} from './holder';
@Component({
selector: 'my-app',
providers: [],
template: `
<div>
<h2>Hello {{name}}</h2>
<holder>
<div>yeey transcluded content {{name}}</div>
</holder>
</div>
`,
directives: [Holder]
})
export class App {
constructor() {
this.name = 'Angular2'
}
}
Вы можете увидеть рабочий пример здесь.
В вашем случае вы можете сделать строку/элемент списка компонентом, который может принять некоторый контент для отображения.
Ответ 2
Я работаю над сценарием многократного использования, который может вам пригодиться. В этом примере это компонент configurator
, который имеет базовую структуру и используется для настройки других компонентов через объект values
(исходит от form.values
).
import {Component, Input, EventEmitter, ViewEncapsulation} from 'angular2/core';
@Component({
encapsulation: ViewEncapsulation.None,
selector: 'configurator',
template: `
<div [hidden]="!active">
<span (click)="active = false">×</span>
<h3>{{ title }}</h3>
<form>
<ng-content></ng-content>
</form>
</div>
`
})
export class ConfiguratorComponent {
@Input() title: string;
@Input() values: any = {};
@Input() emitter: EventEmitter<any>;
public active: boolean = false;
set(key: string, value?: any) {
this.values[key] = value || !this.values[key];
if (this.emitter)
this.emitter.emit(this.values);
}
}
Я использую его в хост-компоненте как дочерний компонент компонента, который он конфигурирует.
@Component({
directives: [
ConfiguratorComponent,
ResourceOneComponent,
],
pipes: [...],
providers: [...],
template: `
<configurator title="Resource One" #cfg [values]="one.values" [emitter]="configEmitter">
<label>Size:
<input type="number" min="0" #size [value]="cfg.values.size" (change)="cfg.set('size', size.value)">
</label>
</configurator>
<resource-one #one [emitter]="configEmitter"></resource-one>
`
})
class HostComponent {
public configEmitter = EmitterService.get('resource_one');
}
Компонент ресурса может быть:
class ResourceOneComponent extends CRUDComponent {
public values: { size: 5 };
private ngOnChanges() {
if (this.emitter)
this.emitter.subscribe(values => {
// use values from configurator
});
}
}
Это сервис, который я использую для связи между компонентами-братьями:
import {Injectable, EventEmitter} from 'angular2/core';
@Injectable()
export class EmitterService {
private static _emitters: { [ID: string]: EventEmitter<any> } = {};
static get(channel: string): EventEmitter<any> {
if (!this._emitters[channel])
this._emitters[channel] = new EventEmitter();
return this._emitters[channel];
}
}
РЕДАКТИРОВАТЬ: Это может быть "излишний" для вашего случая использования (: я только что видел другие ответы, которые действительны для более простых сценариев... Мне нравится держать все как можно больше, поэтому каждый компонент делает одно: я искал способы общения между компонентами многократного использования, и это решение - это то, что работает хорошо. Думал, что было бы полезно поделиться (;
Ответ 3
Вы можете использовать ngSwitch (ng-switch в Angular2), если список разных resource-x-item>
исправлен.
Вы также можете использовать маршрутизацию для динамического добавления компонентов.
Если вышеуказанное не работает для вашего случая использования, вы можете использовать DynamicComponentLoader` (Как использовать angular2 DynamicComponentLoader в ES6?)