Angular 1.5组件:基于组件的应用程序架构

时间:2016-09-03 21:18:48

标签: angularjs typescript components

根据Angular 1.5 documentation组件应该只控制自己的视图和数据。

组件不应更改传递给组件的对象的属性,而应创建原始数据的内部副本,并在此副本发生更改时使用回调通知父组件。

在这个plunk中,我创建了一个小型演示来说明我的问题。

interface IStudent { 
  id: number,
  name: string;
}

/* SERVICE: StudentService */

public class StudentsService {
  static $inject = ['$q'];
  constructor(private $q: ng.IQService) {        
  }

  public getStudents() : ng.IPromise<IStudent[]> {
    return this.$q.when([
      { id: 1, name: 'Adam' },
      { id: 2, name: 'Ben' }
    }]);
  }  
}

/* COMPONENT: student */

class StudentsComponent implements ng.IComponent {
  public template = `<student-list on-selected="$ctrl.onSelected(student)"></student-list>
                     <edit-student student="$ctrl.student" ng-show="$ctrl.student" on-changed="$ctrl.copyChanged(copy)"></edit-student>`;
  public controller = StudentsController;
}

class StudentsController {
  private student: IStudent;

  protected onSelected(student: IStudent) {
    this.student = student;  
  }

  protected copyChanged(copy: IStudent) {
    this.student.name = copy.name;
  }
}

/* COMPONENT: student-list */

class StudentListComponent implements ng.IComponent {
  public template = '<ul><li ng-repeat="student in $ctrl.students"><a ng-click="$ctrl.onClick(student)">{{ student.name }}</a></li></ul>';
  public controller = StudentListController;
  public bindings : any = {
    onSelected: '&'
  }
}

class StudentListController {
  protected students: IStudent[];

  static $inject = ['studentsService'];
  constructor(private studentsService: StudentsService) {
  }

  public $onInit() {
    this.studentsService.getStudents().then(data => this.students = data);
  }

  protected onClick(student: IStudent) {
    this.onSelected({ student: student });
  }
}

/* COMPONENT: edit-student */

class EditStudentComponent implements ng.IComponent {
  public template = `<form class="form">
                       <div class="input-group">
                         <label for="#" class="control-label">Original</label>
                         <input type="text" class="form-control" ng-model="$ctrl.student.name" readonly>
                        </div>
                      </form>
                      <form class="form">
                       <div class="input-group">
                         <label for="#" class="control-label">Copy</label>
                         <input ng-change="$ctrl.changed()" type="text" class="form-control" ng-model="$ctrl.copy.name">
                        </div>
                      </form>`;
  public controller = EditStudentController;
  public bindings :any = {
    student: '<',
    onChanged: '&'
  };
}

class EditStudentController  {
  protected copy: IStudent;

  public $onInit() {
    console.log('EditStudentComponent.$onInit', this.student);
  }

  public $onChange() {
    console.log('EditStudentComponent.$onChange', this.student);
    this.copy = angular.copy(this.student);
  }

  protected changed() {
    console.log('EditStudentController.changed', this.copy);
    this.onChanged({ copy: this.copy });
  }
}

/* Bootstrap */

angular
  .module('app', [])
  .component('students', new StudentsComponent())
  .component('studentList', new StudentListComponent())
  .component('editStudent', new EditStudentComponent())
  .service('studentsService', StudentsService)
;

angular.bootstrap(document, ['app']);

我有一个迭代学生的列表。当用户选择学生时,显示文本框,其中用户可以改变学生的姓名。只要名称发生更改,此更改就会传播到更新列表的父组件。

问题是,在列表中选择学生后,编辑用户组件未初始化,仍显示创建组件时创建的副本的名称(null)。

有人可以告诉我如何修复此插件,以便在单击列表中的学生时,编辑组件会使用所选学生的副本进行初始化吗?

编辑:更改了插件,因为我意外删除了脚本标记而不是样式标记。

1 个答案:

答案 0 :(得分:0)

我认为这个插件代表了我的问题,但唉它没有。插件没有用,因为我实现了$ onChange而不是$ onChanges。我修理了插件,使其按预期工作。

我原来问题的原因是一个完全不同的原因。在我的业务应用程序中,我在编辑组件周围使用了另一个带有ng-transclude指令的组件,如下所示:

tree.setIn(['branch2', 1, 'value'], 'newValue');

由于编辑学生组件是在模态编辑器组件的独立范围内定义的,因此它没有收到对外部范围中的数据变量所做的任何更改(但不知何故,它仍然可以从此外部访问数据范围)。

修改模态编辑器组件以便将数据传递给子组件后,一切都按预期工作:

<modal-editor>
    <edit-student data="$ctrl.data">
    <edit-student>
</modal-editor>