如何在不使用$ scope的情况下在兄弟组件之间传递数据?
我正在这样做一个包含3个子组件的组件:
<header-component> <side-component> <main-component>
主要组件包含英雄列表。 标题组件包含两个button,用于将主组件上的视图切换到列表或网格视图。
我现在的问题是将数据从标题组件传递到主要组件。 所以,当我点击网格button时,主要内容的视图应该改变为网格视图,对于行视图也是如此。
数据如何在angular度1.5的子组件之间传递?
组件方法
我build议你配合使用Angular 2组件方法,并使用input / 输出方法。 如果你这样做,你将能够轻松地迁移到Angular 2,因为组件在概念上将是相同的(仅在语法上有所不同)。 所以这是你做的方式。
所以我们基本上希望标题和主要组件与标题共享一块状态以便能够改变它。 我们可以使用几种方法使其工作,但最简单的方法是使用中间父控制器属性。 所以,让我们假设父控制器(或组件)定义了您希望被头(可读取和修改)和主要(可读取)组件使用的view
属性。
标题组件 :input和输出。
下面是一个简单的头文件组件的样子:
.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>
主要组件 :input。
主要组件比较简单,只需要定义它接受的input即可:
.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>
看一看,玩简单的演示。
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: '<' } })
<script src="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>
尽pipe父组件的方法(通过属性传递数据)是一个完美的有效的实现,但是我们可以使用一个存储工厂以更简单的方式实现同样的事情。
基本上,数据由组件在两个组件作用域中引用,在状态改变时启用UI的反应式更新。
例:
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); }, }; });
然后,在你的组件你应该有这样的东西:
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> ... `
如果你想看到一个工作的例子, 看看这个CodePen 。
这样做也可以启用2个或N个组件之间的通信。 你只需要:
- 注入商店依赖
- 确保将商店数据链接到组件作用域
就像上面的例子( <header-component>
)。
在现实世界中,一个典型的应用程序需要pipe理大量的数据,因此更有意义地以某种方式对数据域进行逻辑分割。 遵循相同的方法, 您可以添加更多的商店工厂 。 例如,要pipe理当前logging的用户信息和外部资源(即目录),可以创build一个UserStore
和一个CatalogStore
,或者是UserModel
和CatalogModel
; 那些实体也是集中处理后端事务的好地方,添加自定义的业务逻辑等等 。 数据pipe理将由Store
工厂独自负责。
请记住,我们正在修改商店数据 。 虽然这种方法简单明了,但可能无法很好地扩展,因为会产生副作用 。 如果你想要更高级的东西(不变性,纯函数,单状态树等等),检查Redux ,或者如果你最终想切换到Angular 2,看看ngrx / store 。
希望这可以帮助! 🙂
你不必这样做Angular 2的方式,因为如果你有时候会迁移…如果这样做是有道理的。
使用自定义事件来实现这一点。 你可以通过你的应用程序使用事件调度器$emit(name, args); or $broadcast(name, args);
传递消息$emit(name, args); or $broadcast(name, args);
$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); });