角度1.5组件绑定,$ onChanges hook& ui-router解析

时间:2016-07-23 22:46:17

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

编辑:我在这里重新提出了这个问题:angular 1.5 component, ui-router resolve, $onChanges lifecycle hook

我将用3个连续的例子说明我的问题。

在第一个组件中,所谓的外部组件拥有一个 myData 对象,该对象具有 name 字段。使用单向绑定将此对象传递给内部组件。外部组件公开了两种更新其对象的方法: modify ,用于更改 name 字段的值, replace 用新的替换它宾语。 当调用 modify 时,内部组件中看到的对象会更新,因为单向绑定是通过引用完成的。当调用 replace 时,内部组件中看到的对象也会更新,因为单向绑定会监视引用修改,并且会调用$ onChanges挂钩。这里一切都很好: - )

angular
.module('app', [])

.component('outer', {
    controller: function () {
      this.myData = {
        name: 'initial'
      };
      this.modify = (value) => { this.myData.name = value; };
      this.replace = (value) => { this.myData = { name: value }; };
    },
  template: `
    <p> outer value : {{ $ctrl.myData.name }} </p>
    <input type="text" ng-model="value">
    <button ng-click="$ctrl.modify(value)"> modify object </button>
    <button ng-click="$ctrl.replace(value)"> replace object </button>
    <hr>
    <inner my-data="$ctrl.myData"></inner>
  `
})

.component('inner', {
  bindings: {
    myData: '<'
  },
  controller: function () {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
  },
  template: `
    <p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
  `
});

在第二个(https://plnkr.co/edit/uEp6L4LuWqTJhwn6uoTN?p=preview)中,外部组件不再声明 myData 对象,而是将其作为单向绑定接收来自解决ui-router组件路由状态。由于 modify replace 方法仍然在外部控制器中以相同的方式完成,所以一切都像前面的例子一样。

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

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

    $stateProvider
    .state('root', {
      url: '/',
      component: 'outer',
      resolve: {
        myData: () => ({ name: 'initial' })
      }
  });
})

.component('outer', {
  bindings: {
    myData: '<'
  },
  controller: function () {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
    this.modify = (value) => { this.myData.name = value; };
    this.replace = (value) => { this.myData = { name: value }; };
  },
  template: `
    <p> outer value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
    <input type="text" ng-model="value">
    <button ng-click="$ctrl.modify(value)"> modify object </button>
    <button ng-click="$ctrl.replace(value)"> replace object </button>
    <hr>
    <inner my-data="$ctrl.myData"></inner>
  `
})

.component('inner', {
  bindings: {
    myData: '<'
  },
  controller: function () {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
  },
  template: `
    <p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
  `
});

在第三个示例(https://plnkr.co/edit/nr9fL9m7gD9k5vZtwLyM?p=preview)中,引入了一个拥有myData对象的服务,并公开了 modify replace 方法。 ui-router状态的解析现在调用服务来获取数据对象。外部控制器调用服务的修改替换方法。在第一次调用替换方法之前,一切都很好。我期待在服务中对象的引用发生更改时触发外部组件的$ onChanges挂钩,但事实并非如此。

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

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

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

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

.component('outer', {
  bindings: {
    myData: '<'
  },
  controller: function (DataService) {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
    this.modify = (value) => { DataService.modify(value); };
    this.replace = (value) => { DataService.replace(value); };
  },
  template: `
    <p> outer value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
    <input type="text" ng-model="value">
    <button ng-click="$ctrl.modify(value)"> modify object </button>
    <button ng-click="$ctrl.replace(value)"> replace object </button>
    <hr>
    <inner my-data="$ctrl.myData"></inner>
  `
})

.component('inner', {
  bindings: {
    myData: '<'
  },
  controller: function () {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
  },
  template: `
    <p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
  `
});

我在这里做错了什么?

精确度:使用 modify 方法时,工作正常。但是,我希望能够在组件中执行绑定对象的本地副本,以避免从那里修改原始对象(确保真正的单向数据流)并使用$ onChanges挂钩来通知对象修改(重新分配)。

编辑:我提出了以下代码(https://plnkr.co/edit/DZqgb4aIi09GWiblFcmR?p=preview),其中获取解析数据的组件不是触发服务中修改的组件。那么问题是:为什么在重新分配已解析的数据对象时,使用者组件中没有触发$ onChanges挂钩?如何通知该组件修改?

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

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

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

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

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

.component('consumer', {
  bindings: {
    myData: '<'
  },
  controller: function () {
    this.nbOnChangesCalls = 0;
    this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
  },
  template: `
    <p> value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
  `
});

理想情况下,使用者组件的$ onChanges方法将以这种方式实现:

this.$onChanges = (changes) => {
  if (changes.myData) this.myData = Object.assign({}, this.myData);
  this.nbOnChangesCalls++;
}

在这种情况下,修改版本不起作用(正如预期的那样,因为组件不再对服务数据对象有引用)并且因为$ onChanges挂钩未被触发(这是我的主要问题!)替换版本也不起作用..

0 个答案:

没有答案