新AngularJS ng-ref指令的缺陷

geo*_*awg 8 angularjs angularjs-components

AngularJS V1.7.1 *的发布引入了新的ng-ref指令.虽然这个新指令使用户能够轻松地做某些事情,但我发现滥用和问题的可能性很大.

NG-REF属性告诉AngularJS发布一个组件的控制器上的当前范围.这对于让诸如音频播放器之类的组件将其API暴露给兄弟组件非常有用.它的播放和停止控制可以很容易地访问.

第一个问题是播放器控件在控制器undefined$onInit功能内.

Initial vm.pl = undefined  <<<< UNDEFINED
Sample = [true,false]
Run Code Online (Sandbox Code Playgroud)

对于依赖于可用数据的代码,我们如何解决这个问题?

DEMO

angular.module("app",[])
.controller("ctrl", class ctrl {
  constructor() {
    console.log("construct")
  }
  $onInit() {
    console.log("onInit", this.pl);
    this.initPL = this.pl || 'undefined';
    this.sample = this.pl || 'undefined';
    this.getSample = () => {
      this.sample = `[${this.pl.box1},${this.pl.box2}]`;
    }
  }
})
.component("player", {
  template: `
    <fieldset>
      $ctrl.box1={{$ctrl.box1}}<br>
      $ctrl.box2={{$ctrl.box2}}<br>
      <h3>Player</h3>
    </fieldset>
  `,
  controller: class player {
    constructor() {
      console.log("player",this);
    }
    $onInit() {
      console.log("pl.init", this)
      this.box1 = true;
      this.box2 = false;
    }
  },
})
Run Code Online (Sandbox Code Playgroud)
<script src="//unpkg.com/angular@1.7.1/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
    Initial vm.pl = {{vm.initPL}}<br>
    Sample = {{vm.sample}}<br>
    <button ng-click="vm.getSample()">Get Sample</button>
    <br>
    <input type="checkbox" ng-model="vm.pl.box1" />
      Box1 pl.box1={{vm.pl.box1}}<br>
    <input type="checkbox" ng-model="vm.pl.box2" />
      Box2 pl.box2={{vm.pl.box2}}<br>
    <br>
    <player ng-ref="vm.pl"></player>
</body>
Run Code Online (Sandbox Code Playgroud)

len*_*ndc 3

获取对组件控制器的引用并不是什么新鲜事,以前的指令允许这样做,这根本不是问题,有必要拥有这样的功能,ng-ref只是您从模板执行此操作的帮助器侧面(与 Angular 2+ 的方式相同)。

\n\n

尽管如此,如果您需要准备好子组件,您应该使用$postLink()而不是$onInit. $postLink在组件与其子组件链接后调用,这意味着,ng-ref当它被调用时将准备好。

\n\n

所以你所要做的就是改变你的onInit样子:

\n\n
\xcc\xb6$\xcc\xb6o\xcc\xb6n\xcc\xb6I\xcc\xb6n\xcc\xb6i\xcc\xb6t\xcc\xb6(\xcc\xb6)\xcc\xb6 \xcc\xb6{\xcc\xb6\n$postLink() {\n    console.log("onInit", this.pl);\n    this.initPL = this.pl || \'undefined\';\n    this.sample = this.pl || \'undefined\';\n    this.getSample = () => {\n      this.sample = `[${this.pl.box1},${this.pl.box2}]`;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

$postLink()- 链接此控制器的元素及其子元素后调用。与 post-link 函数类似,该钩子可用于设置 DOM 事件处理程序并进行直接 DOM 操作。请注意,包含 templateUrl 指令的子元素不会被编译和链接,因为它们正在等待其模板异步加载,并且它们自己的编译和链接已暂停,直到发生这种情况。这个钩子可以被认为类似于Angular 中的ngAfterViewInitngAfterContentInit钩子。由于 AngularJS 中的编译过程相当不同,因此没有直接映射,升级时应小心。

\n\n

参考:了解组件

\n
\n\n

完整的工作片段可以在下面找到(我删除了所有内容console.log以使其更清晰):

\n\n

\r\n
\r\n
\xcc\xb6$\xcc\xb6o\xcc\xb6n\xcc\xb6I\xcc\xb6n\xcc\xb6i\xcc\xb6t\xcc\xb6(\xcc\xb6)\xcc\xb6 \xcc\xb6{\xcc\xb6\n$postLink() {\n    console.log("onInit", this.pl);\n    this.initPL = this.pl || \'undefined\';\n    this.sample = this.pl || \'undefined\';\n    this.getSample = () => {\n      this.sample = `[${this.pl.box1},${this.pl.box2}]`;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\r\n
angular.module("app",[])\r\n.controller("ctrl", class ctrl {\r\n  constructor() {\r\n    //console.log("construct")\r\n  }\r\n  $postLink() {\r\n    //console.log("onInit", this.pl);\r\n    this.initPL = this.pl || \'undefined\';\r\n    this.sample = this.pl || \'undefined\';\r\n    this.getSample = () => {\r\n      this.sample = `[${this.pl.box1},${this.pl.box2}]`;\r\n    }\r\n  }\r\n})\r\n.component("player", {\r\n  template: `\r\n    <fieldset>\r\n      $ctrl.box1={{$ctrl.box1}}<br>\r\n      $ctrl.box2={{$ctrl.box2}}<br>\r\n    </fieldset>\r\n  `,\r\n  controller: class player {\r\n    constructor() {\r\n      //console.log("player",this);\r\n    }\r\n    $onInit() {\r\n      //console.log("pl.init", this)\r\n      this.box1 = true;\r\n      this.box2 = false;\r\n    }\r\n  },\r\n})
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n