Angular2 中的 ViewChildren 装饰器可以与接口一起使用吗? [英] Can the ViewChildren Decorator in Angular2 work with Interfaces?

查看:29
本文介绍了Angular2 中的 ViewChildren 装饰器可以与接口一起使用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据我对 Angular 2 的理解,ViewChildren 装饰器允许组件获取其他组件或指令的查询.当我知道组件的特定类型时,我可以让它在 Typescript 中工作,但是当我只知道组件的接口时,我希望能够获得 QueryList .这样,我就可以遍历视图组件.

例如,在组件中我可能有这个:

@ViewChildren(Box) 形状:QueryList;

其中 Box 是一个具体的 TypeScript 类.我想要的是这个:

@ViewChildren(IShape) 形状:QueryList;

其中 IShape 是 Boxes 或其他组件可能实现的接口.这样视图可以非常动态,我的代码仍然可以工作.有没有推荐的方法来处理这个问题?

解决方案

实际上有一种方法可以做一些类似于您想要做的事情,尽管可能不适用于 Typescript 接口,因为 Günter Zöchbauer 是正确的,他们没有一旦代码被转译为 javascript 就存在.

但是您可以使用父类.父类可能是一个抽象类.现在我想起来,如果接口被转换成运行时命名空间,它也应该可以工作,我不知道它们是否是.

@Component({选择器:'正方形',提供者:[提供(形状,useExisting:forwardRef(()=>Square)]})class Square 扩展了 Shape {}

参考这个讨论.

https://github.com/angular/angular/issues/8580

现在我想给像我一样使用 es5 的人留下我自己的例子,并且为了更彻底的用例演示.我试图平衡额外细节的数量,以便该示例作为一个整体有意义而不会变得无关紧要.

请如果你因为我偏离主题而投反对票,请停止阅读.

我需要在仪表板组件中执行一些自定义调整大小逻辑,并且我希望几种不同类型的图表指令仅在我在父仪表板组件中执行我的自定义调整大小逻辑后才能重新呈现.我的一些图表实际上是组件,它没有引起任何问题.使以下模式在 es5 中工作所需的任何其他内容都是标准的.您不需要在提供给您的 NgModule 的提供程序列表中包含 app.Renderable.

renderable.class.js

(function(app) {app.Renderable = ng.core.Class({构造函数:[function Renderable() {}],渲染:函数(){}});})(window.app || (window.app = {}));

chart-one.directive.js

(function(app) {app.ChartOneDirective = ng.core.Directive({选择器:'画布[图表一]',输入:['配置:图表一'],提供者:[{提供:app.Renderable,useExisting: ng.core.forwardRef(function(){返回 app.ChartOneDirective;}),}]}).班级({扩展:app.Renderable,构造函数:[/* 注入 */function ChartOneDirective(/* 注入 */) {//做东西}],//其他方法渲染:函数(){//渲染图表}});})(window.app || (window.app = {}));

chart-two.directive.js

(function(app) {app.ChartTwoDirective = ng.core.Directive({选择器:'画布[图表二]',输入:['配置:图表二'],提供者:[{提供:app.Renderable,useExisting: ng.core.forwardRef(function(){返回 app.ChartTwoDirective;}),}]}).班级({扩展:app.Renderable,构造函数:[/* 注入 */function ChartTwoDirective(/* 注入 */) {//做东西}],//其他方法渲染:函数(){//渲染图表}});})(window.app || (window.app = {}));

dashboard.component.js

(function(app) {app.DashboardComponent = ng.core.Component({选择器:'仪表板组件',templateUrl : 'components/dashboard/dashboard.component.html',主持人 : {'(window.resize)' : 'rerender()',},查询:{可渲染对象:新的 ng.core.ViewChildren(app.Renderable),//用于调整大小的其他视图子项}}).班级({构造函数:[/* 注入 */function DashboardComponent(/* 注入 */) {//做东西}],调整大小:函数(){//对 dom 内的事物进行自定义大小调整},//其他方法重新渲染:函数(){this.resize();this.renderables.forEach(function(r){r.render();});}});})(window.app || (window.app = {}));

dashboard.component.html

<div class='canvas-wrapper'><canvas [图表一]></canvas></div><div class='canvas-wrapper'><canvas [图表二]></canvas></div><div class='canvas-wrapper'><canvas [图表一]></canvas></div><div #sizeMeToo><div class='canvas-wrapper'><canvas [图表二]></canvas></div><div class='canvas-wrapper'><canvas [图表一]></canvas></div>

现在,在 es5 javascript 中,实际上没有必要扩展 Renderable 类来使其工作.此外,您可以在您的提供者列表中放置多个提供者,从而允许查询您的组件或指令以获取我的多个令牌.因此,您可以说您可以实现"几个接口",以便以经典的 javascript 方式选择 ViewChild,但实际上没有任何保证.

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.

这篇关于Angular2 中的 ViewChildren 装饰器可以与接口一起使用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆