如何在不使用$ scope的情况下在兄弟组件之间传递数据?

时间:2016-03-16 11:09:50

标签: angularjs components angularjs-controlleras

我正在以这种方式制作一个包含3个子组件的组件:

<header-component>
<side-component>
<main-component>

主要组件包含英雄列表。 标题组件包含两个按钮,用于将主组件上的视图切换到列表或网格视图。

我现在遇到的问题是将数据从header-component传递到main组件。因此,当我单击网格按钮时,主内容上的视图应更改为网格视图,对于行视图也是如此。

如何在角度1.5的子组件之间传递数据?

3 个答案:

答案 0 :(得分:83)

组件方法

我建议您与Angular 2组件方法保持一致,并使用inputs / outputs方法。如果这样做,您将能够轻松迁移到Angular 2,因为组件在概念上是相同的(仅在语法上有所不同)。所以这就是你的方式。

因此我们基本上希望标题和主要组件与标题共享一段状态以便能够更改它。我们可以使用几种方法使其工作,但最简单的方法是使用中间父控制器属性。因此,让我们假设父控制器(或组件)定义了您希望由头(可以读取和修改)和主(可以读取)组件使用的view属性。

标题组件:输入和输出。

以下是简单的标题组件的外观:

.component('headerComponent', {
  template: `
    <h3>Header component</h3>
    <a ng-class="{'btn-primary': $ctrl.view === 'list'}" ng-click="$ctrl.setView('list')">List</a>
    <a ng-class="{'btn-primary': $ctrl.view === 'table'}" ng-click="$ctrl.setView('table')">Table</a>
  `,
  controller: function() {
    this.setView = function(view) {
      this.view = view
      this.onViewChange({$event: {view: view}})
    }
  },
  bindings: {
    view: '<',
    onViewChange: '&'
  }
})

这里最重要的部分是绑定。使用view: '<',我们指定header组件将能够读取外部内容并将其绑定为自己控制器的view属性。通过onViewChange: '&'组件定义输出:用于通知/更新外部世界的渠道。标题组件将通过此通道推送一些数据,但它不知道父组件将使用它做什么,它不应该关心。

所以这意味着header控制器可以像

那样使用
<header-component view="root.view" on-view-change="root.view = $event.view"></header-component> 

主要组件:输入。

主要组件更简单,只需要定义它接受的输入:

.component('mainComponent', {
  template: `
    <h4>Main component</h4>
    Main view: {{ $ctrl.view }}
  `,
  bindings: {
    view: '<'
  }
})

父视图

最后这一切都联系在一起:

<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
<main-component view="root.view"></main-component>

看看并玩简单的演示。

&#13;
&#13;
angular.module('demo', [])

.controller('RootController', function() {
  this.view = 'table'
})

.component('headerComponent', {
  template: `
    <h3>Header component</h3>
    <a class="btn btn-default btn-sm" ng-class="{'btn-primary': $ctrl.view === 'list'}" ng-click="$ctrl.setView('list')">List</a>
    <a class="btn btn-default btn-sm" ng-class="{'btn-primary': $ctrl.view === 'table'}" ng-click="$ctrl.setView('table')">Table</a>
  `,
  controller: function() {
    this.setView = function(view) {
      this.view = view
      this.onViewChange({$event: {view: view}})
    }
  },
  bindings: {
    view: '<',
    onViewChange: '&'
  }
})

.component('mainComponent', {
  template: `
    <h4>Main component</h4>
    Main view: {{ $ctrl.view }}
  `,
  bindings: {
    view: '<'
  }
})
&#13;
<script src="https://code.angularjs.org/1.5.0/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />

<div class="container" ng-app="demo" ng-controller="RootController as root">
  
    <pre>Root view: {{ root.view }}</pre>
    
    <header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
    <main-component view="root.view"></main-component>
    
</div>
&#13;
&#13;
&#13;

演示: http://plnkr.co/edit/ODuY5Mp9HhbqA31G4w3t?p=info

以下是我撰写的关于基于组件的设计的博客文章:http://dfsq.info/site/read/angular-components-communication

答案 1 :(得分:15)

虽然父组件方法(通过属性传递数据)是一个完美有效但又很好的实现,我们可以使用商店以更简单的方式实现相同的目标工厂。

基本上,数据由Store保留,angular .module('YourApp') // declare the "Store" or whatever name that make sense // for you to call it (Model, State, etc.) .factory('Store', () => { // hold a local copy of the state, setting its defaults const state = { data: { heroes: [], viewType: 'grid' } }; // expose basic getter and setter methods return { get() { return state.data; }, set(data) { Object.assign(state.data, data); }, }; }); 在两个组件范围中都被引用,在状态发生变化时启用UI的反应性更新。

示例:

angular
    .module('YourApp')
    .component('headerComponent', {
        // inject the Store dependency
        controller(Store) {
            // get the store reference and bind it to the scope:
            // now, every change made to the store data will
            // automatically update your component UI
            this.state = Store.get();

            // ... your code
        },
        template: `
            <div ng-show="$ctrl.state.viewType === 'grid'">...</div>
            <div ng-show="$ctrl.state.viewType === 'row'">...</div>
            ...
        `
    })
    .component('mainComponent', {
        // same here, we need to inject the Store
        controller(Store) {
            // callback for the switch view button
            this.switchViewType = (type) => {
                // change the Store data:
                // no need to notify or anything
                Store.set({ viewType: type });
            };

            // ... your code
        },
        template: `
            <button ng-click="$ctrl.switchViewType('grid')">Switch to grid</button>
            <button ng-click="$ctrl.switchViewType('row')">Switch to row</button>
            ...
        `

然后,在你的组件中你应该有:

<header-component>

如果您想查看一个有效的示例check out this CodePen

这样做,您还可以启用2个或N个组件之间的通信。你只需要:

  1. 注入商店依赖
  2. 确保将商店数据链接到组件范围
  3. 就像上面的示例(UserStore)。

    在现实世界中,典型的应用程序需要管理大量数据,因此以某种方式逻辑分割数据域更有意义。按照相同的方法,您可以添加更多商店工厂。例如,要管理当前记录的用户信息以及外部资源(即目录),您可以构建CatalogStoreUserModel - 或者CatalogModelStore; 这些实体也是集中管理后端,添加自定义业务逻辑等内容的好地方。然后,数据管理将由class ABC { public void MethodA() { Console.WriteLine("In method A"); } public void MethodB() { Console.WriteLine("In method B"); } public void MethodC() { Console.WriteLine("In method C"); } } class PQR { public void MethodP() { Console.WriteLine("In method P"); } public void MethodQ() { Console.WriteLine("In method Q"); } public void MethodR() { Console.WriteLine("In method R"); } } 工厂单独负责。

    请注意,我们正在改变商店数据。虽然这种方法简单明了,但可能无法很好地扩展,因为它会产生side effects。如果你想要更高级的东西(不变性,纯函数,单状态树等),请查看Redux,或者如果你最终想切换到Angular 2,请查看ngrx/store

    希望这有帮助! :)

      

    你不必以Angular 2的方式去做,因为以防万一   你有时会迁移......如果你有意义的话,那就去做吧。

答案 2 :(得分:6)

使用自定义事件来实现此目的。 您可以使用事件调度程序$emit(name, args); or $broadcast(name, args);在您的应用程序中传递消息 您可以使用方法$ on(name,listener);

来监听此事件

希望有所帮助

价: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit

实施例: 您可以从标题组件

通知如下所示的更改
$rootScope.$emit("menu-changed", "list");

您可以听取主要组件指令中的更改,例如

$rootScope.$on("menu-changed", function(evt, arg){
  console.log(arg);
});