【问题标题】:angular 1.5 component, ui-router resolve, $onChanges lifecycle hookangular 1.5 组件,ui-router 解析,$onChanges 生命周期钩子
【发布时间】:2016-12-01 04:40:12
【问题描述】:

在以下示例 (plunker) 中,ui-router 状态路由到具有 data 对象和 replace 的 app 组件em> 使用给定值用新对象替换此对象的方法。在它的模板中,它有:

  • 一个 editor 组件,通过回调绑定 ('&') 触发 replace 方法
  • 一个 display 组件通过单向绑定 ('data 对象,在 $onChanges 生命周期挂钩被触发时制作本地副本,并显示对象的内容

一切正常且符合预期:-)

angular
.module('app', ['ui.router'])

.config(($urlRouterProvider, $stateProvider) => {
    $urlRouterProvider.otherwise('/');

    $stateProvider
    .state('root', {
      url: '/',
      component: 'app'
  });
})

.component('app', {
  controller: function () {
    this.data = { name: 'initial' };
    this.replace = (value) => { this.data = { name: value }; };
  },
  template: `
    <editor on-replace="$ctrl.replace(value)"></editor>
    <display data="$ctrl.data"></display>
  `
})

.component('editor', {
  bindings: {
    onReplace: '&'
  },
  controller: function () {
    this.replace = (value) => this.onReplace({value});
  },
  template: `
    <input type="text" ng-model="value">
    <button ng-click="$ctrl.replace(value)"> replace object </button>
  `
})

.component('display', {
  bindings: {
    data: '<'
  },
  controller: function () {
    this.$onChanges = (changes) => {
      if (changes.data) {
        this.data = Object.assign({}, this.data);
      }
    };
  },
  template: `
    <p> value : {{ $ctrl.data.name }} </p>
  `
});

我对以下第二个示例 (plunker) 有疑问。完全一样的设置,只是 app 组件不再管理 data 本身,而是通过 1-way 接收一个 data 对象binding ('app 组件的 $onChanges 钩子被触发(就像 display 组件的数据对象 em>app 组件被重新分配),但事实并非如此。有没有人解释一下?

let data = { name: 'initial' };
const replace = (value) => { data = { name: value }; };

angular
.module('app', ['ui.router'])

/*.factory('DataService', function () {
  let data = { name: 'initial' };
  return {
    getData: () => data,
    replace: (value) => { data = { name: value }; }
  };
})*/

.config(($urlRouterProvider, $stateProvider) => {
    $urlRouterProvider.otherwise('/');

    $stateProvider
    .state('root', {
      url: '/',
      component: 'app',
      resolve: {
        data: () => data /*(DataService) => DataService.getData()*/
      }
  });
})

.component('app', {
  bindings: {
    data: '<'
  },
  controller: function (/*DataService*/) {
    this.$onChanges = (changes) => {
      if (changes.data) {
        this.data = Object.assign({}, this.data);
      }
    };
    this.replace = (value) => { replace(value); }; /*(value) => { DataService.replace(value); };*/
  },
  template: `
    <editor on-replace="$ctrl.replace(value)"></editor>
    <display data="$ctrl.data"></consumer>
  `
})

.component('editor', {
  bindings: {
    onReplace: '&'
  },
  controller: function () {
    this.replace = (value) => this.onReplace({value});
  },
  template: `
    <input type="text" ng-model="value">
    <button ng-click="$ctrl.replace(value)"> replace object </button>
  `
})

.component('display', {
  bindings: {
    data: '<'
  },
  controller: function () {
    this.$onChanges = (changes) => {
      if (changes.data) {
        this.data = Object.assign({}, this.data);
      }
    };
  },
  template: `
    <p> value : {{ $ctrl.data.name }} </p>
  `
});

【问题讨论】:

  • 嗨皮埃尔,我正面临这个确切的问题 - 我想知道你从那以后发现了什么?

标签: javascript angularjs angular-ui-router angular-component-router


【解决方案1】:

在后台,UI-ROUTER 创建了 HTML,并一次性绑定到父作用域上的 $resolve.data

<app data="::$resolve.data" class="ng-scope ng-isolate-scope">
  <editor on-replace="$ctrl.replace(value)" class="ng-isolate-scope">
    <input type="text" ng-model="value" class="ng-pristine ng-untouched ng-valid ng-empty">
    <button ng-click="$ctrl.replace(value)"> replace object </button>
  </editor>
  <display data="$ctrl.data" class="ng-isolate-scope">
    <p class="ng-binding"> value : initial </p>
  </display>
</app>

调用app 组件中的$onChanges 钩子的观察者观察父作用域的$resolve.data 属性。它不会对隔离范围的$ctrl.data 属性的更改做出反应。由于它是一次性绑定,因此 $onChanges 挂钩仅在变量初始化时调用,而不是在后续更改时调用。


通过 RxJZ 使用服务

与其试图让$onChange 去做一些它不是为它设计的事情,不如用RxJS Extensions for Angular 构建一个服务。

app.factory("DataService", function(rx) {
  var subject = new rx.Subject(); 
  var data = "Initial";

  return {
      set: function set(d){
        data = d;
        subject.onNext(d);
      },
      get: function get() {
        return data;
      },
      subscribe: function (o) {
         return subject.subscribe(o);
      }
  };
});

然后只需订阅更改即可。

app.controller('displayCtrl', function(DataService) {
  var $ctrl = this;

  $ctrl.data = DataService.get();
  var subscription = DataService.subscribe(function onNext(d) {
      $ctrl.data = d;
  });

  this.$onDestroy = function() {
      subscription.dispose();
  };
});

DEMO on PLNKR

【讨论】:

  • 非常感谢您的洞察力。就我而言, $resolve.data 是全局数据对象(或从服务中获取的对象)。如果我理解得很好,如果解析数据的绑定是 1-way 而不是 1-time 绑定,我期望的 $onChanges 钩子会被调用?
  • 不,当 parent 范围的属性发生变化时,观察者会调用 $onChange 挂钩。不监视组件的 child 隔离范围的更改。 &lt; 绑定是从 parentchild 的单向绑定。 $onChange 钩子对组件的 external 更改做出反应。解析器函数仅在视图的生命周期内调用一次。一次性绑定是对解析器数据进行单向绑定的合适选择。
  • 但是被解析的dataobject app 组件的外部。它要么是一个全局变量,要么来自DataService。所以如果它是单向绑定的,我不明白为什么重新分配这个对象(在全局或服务范围内)不会触发app 组件的$onChanges 钩子?
  • 有多个变量引用同一个对象。当您更新 全局变量 指向的引用时,它不会更新其他变量指向的内容。 resolve.data 变量仍然引用旧对象。
猜你喜欢
  • 2016-07-18
  • 2018-07-24
  • 2017-11-22
  • 2017-11-14
  • 2020-03-15
  • 1970-01-01
  • 1970-01-01
  • 2018-08-30
相关资源
最近更新 更多