【问题标题】:(Angular upgrade) AngularJS component's binding should be undefined(Angular 升级) AngularJS 组件的绑定应该是未定义的
【发布时间】:2021-04-07 20:28:38
【问题描述】:

目前试图弄清楚为什么会发生这种情况,但如果我在 NG1 组件上有一个可选的函数绑定 (&?),那么 NG2 将在其上放置一个 EventEmitter:/

这不是我所期望的,并且破坏了我们在通常为 null/undefined 时所做的一些代码。

以下是我们所拥有的示例:

class MyController {
  public onInit: () => string;
  public onAction: () => void;

  public resultFromNg2: string;
  public onActionHasValue: boolean;
  public typeOfOnAction: string;

  public $onInit() {
    this.resultFromNg2 = this.onInit();
    this.onActionHasValue = this.onAction != null;
    this.typeOfOnAction = typeof this.onAction;
  }
}

export const ng1DemoComponent = {
  selector: "ng1-demo",
  template: `
    <div>Hello, {{ $ctrl.username }}!</div>
    <div>resultFromNg2: {{ $ctrl.resultFromNg2 }}</div>
    <div>onActionHasValue: {{ $ctrl.onActionHasValue }} - typeof: {{ $ctrl.typeOfOnAction }}</div>
  `,
  bindings: {
    username: "<",
    onInit: "&?",
    onAction: "&?"
  },
  controller: MyController
};

@Directive({
  selector: ng1DemoComponent.selector
})
export class Ng1DemoComponentFacade extends UpgradeComponent {
  @Input() username: string;
  @Input() onInit: () => string;
  @Input() onAction: () => void;

  constructor(elementRef: ElementRef, injector: Injector) {
    super(ng1DemoComponent.selector, elementRef, injector);
  }
}

可测试环境:https://stackblitz.com/edit/angular-hybrid-upgrade-dpjv4p?file=ng1%2Fng1-demo.component.ts

在此代码中,我希望将 onAction 设置为 undefined,以便我可以正确分配我的其他属性,但目前它不起作用:/

我也尝试删除?,所以从&amp;?&amp;,但现在可能是NG1 提供angular.noop ..

关于如何在不重写内容的情况下完成这项工作的任何想法?

非常感谢您提供的任何帮助!

注意:我们希望将一部分代码库保留在 NG1 中,因为我们现在无法使用现有代码库转换所有代码库。

【问题讨论】:

    标签: angularjs angular angular-upgrade


    【解决方案1】:

    Angular 的 UpgradeComponent 构造函数调用这个:

    private initializeOutputs() {
      // Initialize the outputs for `=` and `&` bindings
      this.bindings.twoWayBoundProperties.concat(this.bindings.expressionBoundProperties)
          .forEach(propName => {
            const outputName = this.bindings.propertyToOutputMap[propName];
            (this as any)[outputName] = new EventEmitter(); // <--- sets it to an EventEmitter
          });
    }
    

    这会将定义在 bindings 对象上的每个 &amp; 绑定设置为 EventEmitter,即使(不幸的是)绑定是可选的且当前未分配。

    不过,您可以做的一件事是使用 NG1 组件控制器中的 $onChanges() 生命周期挂钩来帮助您确定哪些绑定已分配,哪些尚未分配。

    这个钩子在$onInit() 之前触发,并被一个对象调用,该对象保存与currentValue 和previousValue 的所有单向绑定的变化。

    这意味着该对象仅对已分配值的每个绑定都有一个属性。因此,在您的情况下,“更改”对象将具有 usernameonInit 属性,但 不会 具有 onAction 属性。

    当然,您可以通过多种方式利用它,但一种简单的方法是:

    在您的控制器上创建一个assignedBindings 成员,并在$onChanges() 中为其分配传入的changes 对象:

    // ng1DemoComponent MyController
    
    assignedBindings;
    
    public $onChanges(changes) {
      this.assignedBindings = changes;
    }
    

    所以this.assignedBindings 看起来像这样:

    {
      onInit: { previousValue: undefined, /* ... */ }, // <-- SimpleChange instance
      username: { previousValue: undefined, /* ... */ } // <-- SimpleChange instance
    }
    

    然后更新$onInit() 以检查任何您只想设置为EventEmitters 的绑定属性(如果它们实际已分配),并将属性设置为null(如果未分配):

    // ng1DemoComponent MyController
    
    public $onInit() {
      this.resultFromNg2 = this.onInit();
    
      if (!this.assignedBindings.onInit) { // <--- verify onInit assigned
        this.onInit = null;
      }
    
      if (!this.assignedBindings.onAction) { // <--- verify onAction assigned
        this.onAction = null;
      }
    
      this.onActionHasValue = this.onAction != null;
      this.typeOfOnAction = typeof this.onAction;
    }
    

    您可能需要进一步检查每个SimpleChange 实例的currentValue 是否会随时间变化,但您明白了。

    Here's a fork of your StackBlitz 展示了这种方法。它可能有点hacky,但它似乎工作。

    顺便说一句,感谢您创建 StackBlitz — 这对尝试调查此问题有很大帮助。

    【讨论】:

    • 感谢您对此的详细回答!我不明白的是,他们为每个&amp; 绑定放置了一个new EventEmitter,但是我的具有返回值的onInit 绑定正常工作,通常不适用于EventEmitter,所以我有点很感兴趣。
    猜你喜欢
    • 2017-07-14
    • 2017-05-27
    • 1970-01-01
    • 2017-08-01
    • 2019-10-20
    • 2017-06-24
    • 2018-03-20
    • 1970-01-01
    相关资源
    最近更新 更多