新AngularJS ng-ref指令的陷阱 [英] Pitfalls of the New AngularJS ng-ref Directive

查看:158
本文介绍了新AngularJS ng-ref指令的陷阱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

AngularJS V1.7.1的发布 ng-ref指令.尽管此新指令使用户可以轻松地执行某些操作,但我看到了滥用和问题的巨大潜力.

The release of AngularJS V1.7.1* introduces the new ng-ref directive. While this new directive enables users to easily do certain things, I see great potential for abuse and problems.

ng-ref 属性告诉AngularJS发布控制器当前范围内的组件.这对于使诸如音频播放器之类的组件将其API暴露给同级组件很有用.可以轻松访问其播放和停止控件.

The ng-ref attribute tells AngularJS to publish the controller of a component on the current scope. This is useful for having a component such as an audio player expose its API to sibling components. Its play and stop controls can be easily accessed.

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

The first problem is the player controls are undefined inside the $onInit function of the controller.

Initial vm.pl = undefined  <<<< UNDEFINED
Sample = [true,false]

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

For code that depends on data being available, how do we fix this?

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;
    }
  },
})

<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>

推荐答案

将ref引用到组件控制器并不是什么新鲜事,早在今天的指令中就允许使用它,这根本不是问题,必须具有这样的功能,ng-ref只是您从模板端执行此操作的帮手(与angular 2+相同).

Getting ref to components controller isn't new, directives back in the day allowed it and that wasn't a problem at all, it's necessary to have such feature, ng-ref is just a helper for you to do this from the template side (the same way angular 2+ does).

但是,如果您需要准备好子组件,则应使用$postLink()而不是$onInit. $postLink在组件与其子级链接之后被调用,这意味着ng-ref在被调用时将准备就绪.

Nevertheless, if you need the child components ready you should use $postLink() instead of $onInit. $postLink is called after the component is linked with his children, which means, the ng-ref will be ready when it gets called.

所以您要做的就是像这样更改您的onInit:

So all you have to do is change your onInit like so:

̶$̶o̶n̶I̶n̶i̶t̶(̶)̶ ̶{̶
$postLink() {
    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}]`;
    }
}

$postLink()-在链接此控制器的元素及其子元素之后调用.与后链接功能类似,此挂钩可用于设置DOM事件处理程序并直接进行DOM操作.注意,包含templateUrl指令的子元素将不会被编译和链接,因为它们正在等待异步加载其模板,并且它们自己的编译和链接已被挂起,直到发生这种情况为止.可以将此钩子视为类似于Angular中的ngAfterViewInitngAfterContentInit钩子.由于AngularJS中的编译过程非常不同,因此没有直接映射,因此升级时应格外小心.

$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs. This hook can be considered analogous to the ngAfterViewInit and ngAfterContentInit hooks in Angular. Since the compilation process is rather different in AngularJS there is no direct mapping and care should be taken when upgrading.

参考资料:了解组件

完整的代码段可以在下面找到(我删除了所有console.log使其更加清晰):

The full working snippet can be found bellow (I removed all console.log to make it clearer):

angular.module("app",[])
.controller("ctrl", class ctrl {
  constructor() {
    //console.log("construct")
  }
  $postLink() {
    //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>
    </fieldset>
  `,
  controller: class player {
    constructor() {
      //console.log("player",this);
    }
    $onInit() {
      //console.log("pl.init", this)
      this.box1 = true;
      this.box2 = false;
    }
  },
})

<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>
    <player ng-ref="vm.pl"></player>
  </body>

这篇关于新AngularJS ng-ref指令的陷阱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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