将Angular组件从许多输入/输出重构为单个配置对象 [英] Refactoring Angular components from many inputs/outputs to a single config object

查看:63
本文介绍了将Angular组件从许多输入/输出重构为单个配置对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的组件通常从具有多个@Input@Output属性开始.当我添加属性时,切换到单个配置对象作为输入似乎更干净.

My components often start out by having multiple @Input and @Output properties. As I add properties, it seems cleaner to switch to a single config object as input.

例如,这是一个具有多个输入和输出的组件:

For example, here's a component with multiple inputs and outputs:

export class UsingEventEmitter implements OnInit {
    @Input() prop1: number;
    @Output() prop1Change = new EventEmitter<number>();
    @Input() prop2: number;
    @Output() prop2Change = new EventEmitter<number>();

    ngOnInit() {
        // Simulate something that changes prop1
        setTimeout(() => this.prop1Change.emit(this.prop1 + 1));
    }
}

及其用法:

export class AppComponent {
    prop1 = 1;

    onProp1Changed = () => {
        // prop1 has already been reassigned by using the [(prop1)]='prop1' syntax
    }

    prop2 = 2;

    onProp2Changed = () => {
        // prop2 has already been reassigned by using the [(prop2)]='prop2' syntax
    }
}

模板:

<using-event-emitter 
    [(prop1)]='prop1'
    (prop1Change)='onProp1Changed()'
    [(prop2)]='prop2'
    (prop2Change)='onProp2Changed()'>
</using-event-emitter>


随着属性数量的增加,似乎切换到单个配置对象可能会更干净.例如,这是一个带有单个配置对象的组件:


As the number of properties grows, it seems that switching to a single configuration object might be cleaner. For example, here's a component that takes a single config object:

export class UsingConfig implements OnInit {
    @Input() config;

    ngOnInit() {
        // Simulate something that changes prop1
        setTimeout(() => this.config.onProp1Changed(this.config.prop1 + 1));
    }
}

及其用法:

export class AppComponent {
    config = {
        prop1: 1,

        onProp1Changed(val: number) {
            this.prop1 = val;
        },

        prop2: 2,

        onProp2Changed(val: number) {
            this.prop2 = val;
        }
    };
}

模板:

<using-config [config]='config'></using-config>


现在,我可以通过多层嵌套组件传递配置对象引用.使用config的组件将调用诸如config.onProp1Changed(...)之类的回调,这将导致config对象重新分配新值.因此,似乎我们仍然有单向数据流.另外,添加和删除属性不需要更改中间层.


Now I can just pass the config object reference through multiple layers of nested components. The component using the config would invoke callbacks like config.onProp1Changed(...), which causes the config object to do the reassignment of the new value. So it seems we still have one-way data flow. Plus adding and removing properties doesn't require changes in intermediate layers.

将单个配置对象作为组件的输入,而不是多个输入和输出,是否有不利之处?避免像这样避免@OutputEventEmitter会导致以后出现任何问题吗?

Are there any downsides to having a single config object as an input to a component, instead of having multiple input and outputs? Will avoiding @Output and EventEmitter like this cause any issues that might catch up to me later?

推荐答案

我想说对Input使用单个配置对象可能是可以的,但是您应该始终坚持使用Output. Input定义您的组件从外部需要什么,其中一些可能是可选的.但是,Output完全是组件的业务,应在其中定义.如果您依靠用户来传递这些功能,则要么必须检查undefined函数,要么就继续调用这些函数,就好像它们总是在config中传递一样;如果有的话,使用这些组件也很麻烦.即使用户不需要它们,也可以定义许多事件.因此,始终在组件中定义您的Output并发出您需要发出的任何东西.如果用户不将那些事件绑定到函数,那很好.

I would say it could be OK to use single config objects for Inputs but you should stick to Outputs at all the time. Input defines what your component requires from outside and some of those may be optional. However, Outputs are totally component's business and should be defined within. If you rely on users to pass those functions in, you either have to check for undefined functions or you just go ahead and call the functions as if they are ALWAYS passed within config which may be cumbersome to use your component if there are too many events to define even if the user does not need them. So, always have your Outputs defined within your component and emit whatever you need to emit. If users don't bind a function those event, that's fine.

此外,我认为对Input使用单个config并不是最佳实践.它隐藏了真正的输入,用户可能必须查看代码或文档中的内容才能确定应该传递的内容.但是,如果分别定义了Input,则用户可以使用语言服务

Also, I think having single config for Inputs is not the best practice. It hides the real inputs and users may have to look inside of your code or the docs to find out what they should pass in. However, if your Inputs are defined separately, users can get some intellisense with tools like Language Service

此外,我认为它也可能破坏变更检测策略.

Also, I think it may break change detection strategy as well.

让我们看下面的示例

@Component({
    selector: 'my-comp',
    template: `
       <div *ngIf="config.a">
           {{config.b + config.c}}
       </div>
    `
})
export class MyComponent {
    @Input() config;
}

让我们使用它

@Component({
    selector: 'your-comp',
    template: `
       <my-comp [config]="config"></my-comp>
    `
})
export class YourComponent {
    config = {
        a: 1, b: 2, c: 3
    };
}

对于单独的输入

@Component({
    selector: 'my-comp',
    template: `
       <div *ngIf="a">
           {{b + c}}
       </div>
    `
})
export class MyComponent {
    @Input() a;
    @Input() b;
    @Input() c;
}

让我们用这个

@Component({
    selector: 'your-comp',
    template: `
       <my-comp 
          [a]="1"
          [b]="2"
          [c]="3">
       </my-comp>
    `
})
export class YourComponent {}

如上所述,您必须查看YourComponent的代码以查看要传入的值.此外,还必须在所有位置键入config才能使用这些Input.另一方面,您可以清楚地看到在第二个示例中更好地传递了哪些值.如果您使用的是语言服务

As I stated above, you have to look at the code of YourComponent to see what values you are being passed in. Also, you have to type config everywhere to use those Inputs. On the other hand, you can clearly see what values are being passed in on the second example better. You can even get some intellisense if you are using Language Service

另一件事是,第二个示例将更好地扩展.如果需要添加更多Input,则必须一直编辑config,这可能会破坏组件.但是,在第二个示例中,很容易添加另一个Input,并且您无需触摸工作代码.

Another thing is, second example would be better to scale. If you need to add more Inputs, you have to edit config all the time which may break your component. However, on the second example, it is easy to add another Input and you won't need to touch the working code.

最后但并非最不重要的一点是,您无法真正以自己的方式提供双向绑定.您可能知道,如果在Input中名为dataOutput中名为dataChange,则组件的使用者可以使用双向绑定糖语法和简单类型

Last but not least, you cannot really provide two-way bindings with your way. You probably know that if you have in Input called data and Output called dataChange, consumers of your component can use two-way binding sugar syntax and simple type

<your-comp [(data)]="value">

当您使用

this.dataChange.emit(someValue)

希望这可以澄清我对单个Input

Hope this clarifies my opinions about single Input

修改

我认为单个Input存在一个有效的情况,其中内部也定义了一些function.如果您要开发类似图表组件的程序,而该程序通常需要复杂的选项/配置,那么最好使用单个Input.这是因为该输入设置一次且永不更改,因此最好将图表选项放在一个位置.此外,用户可能会传递一些功能来帮助您绘制图例,工具提示,x轴标签,y轴标签等. 像这样的输入在这种情况下会更好

I think there is a valid case for a single Input which also has some functions defined inside. If you are developing something like a chart component which often requires complex options/configs, it is actually better to have single Input. It is because, that input is set once and never changes and it is better to have options of your chart in a single place. Also, the user may pass some functions to help you draw legends, tooltips, x-axis labels, y-axis labels etc. Like having an input like following would be better for this case

export interface ChartConfig {
    width: number;
    height: number;
    legend: {
       position: string,
       label: (x, y) => string
    };
    tooltip: (x, y) => string;
}

...

@Input() config: ChartConfig;

这篇关于将Angular组件从许多输入/输出重构为单个配置对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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