The way I understand Angular 2, the ViewChildren
decorator allows a Component to get a Query for other Components or Directives. I can get this to work in Typescript when I know the specific Type of the Component, but I would like to be able to get a QueryList
when I just know the interface of the component. That way, I can iterate through the view components.
For example, in the component I may have this:
@ViewChildren(Box) shapes: QueryList<Box>;
where Box is a concrete TypeScript class. What I would like to have is this:
@ViewChildren(IShape) shapes: QueryList<IShape>;
where IShape
is an interface that Boxes or other Components may implement. That way the view can be very dynamic and my code will still work. Is there a recommended way to handle this?
There is in fact a way to do something like what you are trying to do, albeit maybe not with Typescript interfaces, as Günter Zöchbauer is correct that they do not exist as such once the code is transpiled to javascript.
You can use a parent class however. The parent can probably be an abstract class. Now that I think about it, interfaces should work too IF they are transpiled into the runtime namespace, which I do not know if they are.
@Component({
selector: 'square',
providers: [provide(Shape, useExisting: forwardRef( ()=>Square )]
})
class Square extends Shape {}
Refer to this discussion.
https://github.com/angular/angular/issues/8580
Now I want to leave my own example below for those using es5 like me, and for the sake of a more thorough use-case demonstration. I tried to balance the amount of extra detail such that the example makes sense as a whole without getting extraneous.
PLEASE if you are going to down vote me for going off topic, just stop reading here.
I needed to do some custom resize logic in a dashboard component, and I wanted several different types of chart directive to rerender themselves only after I performed my custom resize logic in the parent dashboard component. Some of my charts were components actually and it caused no problems. Anything else you need to make the following pattern work in es5 is standard. You do not need to include app.Renderable in the list of providers given to your NgModule.
renderable.class.js
(function(app) {
app.Renderable = ng.core.Class({
constructor : [function Renderable() {}],
render : function() {}
});
})(window.app || (window.app = {}));
chart-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 = {}));
chart-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>
Now, in es5 javascript, it is actually unnecessary to extend the Renderable class in order for this to work. Furthermore, you can put more than one provider in your provider list, and thus allow your component or directive to be queried for my multiple tokens. Thus you could say you can "implement" several "interfaces" for the purposes of ViewChild selection in the classic javascript fashion of nothing being actually guaranteed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With