Может ли декоратор ViewChildren в Angular2 работать с интерфейсами?
Насколько я понимаю Angular 2, ViewChildren
Декоратор позволяет Компоненту получить Запрос для других Компонентов или Директив. Я могу заставить это работать в Typescript, когда я знаю конкретный тип компонента, но я хотел бы иметь возможность получить QueryList
когда я просто знаю интерфейс компонента. Таким образом, я могу перебирать компоненты представления.
Например, в компоненте у меня может быть это:
@ViewChildren(Box) shapes: QueryList<Box>;
где Box - это конкретный класс TypeScript. То, что я хотел бы иметь это:
@ViewChildren(IShape) shapes: QueryList<IShape>;
где IShape
это интерфейс, который Boxes или другие Компоненты могут реализовать. Таким образом, представление может быть очень динамичным, и мой код все еще будет работать. Есть ли рекомендуемый способ справиться с этим?
4 ответа
На самом деле существует способ сделать что-то похожее на то, что вы пытаетесь сделать, хотя, возможно, не с интерфейсами Typescript, так как Гюнтер Цохбауэр прав, что их не существует как таковые, как только код переносится в javascript.
Однако вы можете использовать родительский класс. Родитель, вероятно, может быть абстрактным классом. Теперь, когда я думаю об этом, интерфейсы должны работать тоже, если они перенесены в пространство имен времени выполнения, которое я не знаю, если они есть.
@Component({
selector: 'square',
providers: [provide(Shape, useExisting: forwardRef( ()=>Square )]
})
class Square extends Shape {}
Обратитесь к этому обсуждению.
https://github.com/angular/angular/issues/8580
Теперь я хочу оставить свой собственный пример ниже для тех, кто использует es5, как я, и для более тщательной демонстрации вариантов использования. Я попытался сбалансировать количество дополнительных деталей так, чтобы пример имел смысл в целом, не становясь посторонним.
ПОЖАЛУЙСТА, если вы собираетесь отказаться от голосования за то, что я не по теме, просто перестаньте читать здесь.
Мне нужно было сделать некоторую настраиваемую логику изменения размера в компоненте панели мониторинга, и я хотел, чтобы несколько различных типов директив диаграммы перерисовывали себя только после того, как я выполнил свою настраиваемую логику изменения размера в родительском компоненте панели мониторинга. Некоторые из моих графиков были компонентами, и это не вызывало проблем. Все, что вам нужно, чтобы следующий шаблон работал в es5, является стандартным. Вам не нужно включать app.Renderable в список поставщиков, предоставленный вашему NgModule.
renderable.class.js
(function(app) {
app.Renderable = ng.core.Class({
constructor : [function Renderable() {}],
render : function() {}
});
})(window.app || (window.app = {}));
диаграмма-one.directive.js
(function(app) {
app.ChartOneDirective = ng.core.Directive({
selector : 'canvas[chart-one]',
inputs : ['config:chart-one'],
providers : [{
provide: app.Renderable,
useExisting: ng.core.forwardRef(function(){
return app.ChartOneDirective;
}),
}]
}).Class({
extends : app.Renderable,
constructor : [/* injections */ function ChartOneDirective(/* injections */) {
// do stuff
}],
// other methods
render : function() {
// render the chart
}
});
})(window.app || (window.app = {}));
диаграмма-two.directive.js
(function(app) {
app.ChartTwoDirective = ng.core.Directive({
selector : 'canvas[chart-two]',
inputs : ['config:chart-two'],
providers : [{
provide: app.Renderable,
useExisting: ng.core.forwardRef(function(){
return app.ChartTwoDirective;
}),
}]
}).Class({
extends : app.Renderable,
constructor : [/* injections */ function ChartTwoDirective(/* injections */) {
// do stuff
}],
// other methods
render : function() {
// render the chart
}
});
})(window.app || (window.app = {}));
dashboard.component.js
(function(app) {
app.DashboardComponent = ng.core.Component({
selector : 'dashboard-component',
templateUrl : 'components/dashboard/dashboard.component.html',
host : {
'(window.resize)' : 'rerender()',
},
queries : {
renderables : new ng.core.ViewChildren(app.Renderable),
// other view children for resizing purposes
}
}).Class({
constructor : [/* injections */ function DashboardComponent(/* injections */) {
// do stuff
}],
resize : function() {
// do custom sizing of things within the dom
},
// other methods
rerender : function() {
this.resize();
this.renderables.forEach(function(r){
r.render();
});
}
});
})(window.app || (window.app = {}));
dashboard.component.html
<div #sizeMe>
<div class='canvas-wrapper'><canvas [chart-one]></canvas></div>
<div class='canvas-wrapper'><canvas [chart-two]></canvas></div>
<div class='canvas-wrapper'><canvas [chart-one]></canvas></div>
<div #sizeMeToo>
<div class='canvas-wrapper'><canvas [chart-two]></canvas></div>
<div class='canvas-wrapper'><canvas [chart-one]></canvas></div>
</div>
</div>
Теперь в es5 javascript фактически нет необходимости расширять класс Renderable, чтобы это работало. Кроме того, вы можете добавить более одного поставщика в список поставщиков и, таким образом, разрешить запрашивать ваш компонент или директиву для моих нескольких токенов. Таким образом, вы могли бы сказать, что вы можете "реализовать" несколько "интерфейсов" для целей выбора ViewChild в классическом стиле javascript, и на самом деле ничего не гарантировано.
Нет, информация об интерфейсе не существует во время выполнения и, следовательно, не может использоваться для запроса различных компонентов, которые реализуют определенный интерфейс.
Поддерживается только один тип или список переменных шаблона, таких как
@ViewChildren('a,b,c,d') children;
<div #a>a</div>
<div #b>a</div>
<div #c>a</div>
<div #d>a</div>
<div #d>a</div>
<div #e>a</div>
приведет к 5 ссылкам в children
Текущий (начиная с Angular 12) способ сделать что-то подобное (к сожалению, все еще полагающийся на абстрактный базовый класс, а не на интерфейс) выглядит следующим образом:
abstract class Shape {
...
}
@Component({
providers: [{provide: Shape, useExisting: forwardRef(() => Box)}],
...
})
class Box extends Shape {
...
}
(
forwardRef
необходимо из-за того, что класс ссылается на себя внутри атрибута класса.)
Box
потомки компонентов могут быть запрошены с помощью:
@ViewChildren(Shape) private shapes;
...
this.shapes.toArray().forEach(shape => {
...
});
Декоратор ViewChildren в Angular2 может работать только с классами, а не с интерфейсами. Это связано с тем, что интерфейсы можно использовать только для проверки типов, а не для фактического наследования классов.