angular 1.5 component,ui-router resolve,$ onChanges生命周期钩子

时间:2016-07-27 09:01:18

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

在以下示例(plunker)中,ui-router状态路由到 app 组件,该组件具有 data 对象和 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 组件不再管理数据本身,而是通过单向接收数据对象绑定('&lt;'),被定义为ui-router状态的解析(可以在注释中看到,我使用全局对象和方法测试,并通过服务进行交互)。当重新分配这个已解析的对象时,我希望触发 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>
  `
});

1 个答案:

答案 0 :(得分:2)

在幕后,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>

调用$onChanges组件中的app挂钩的观察者会监视父作用域的$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