我正在以这种方式制作一个包含 3 个子组件的组件:
<header-component>
<side-component>
<main-component>
主要组件包含英雄列表。标题组件包含两个按钮,假设将主组件上的视图切换到列表或网格视图。
我现在遇到的问题是将数据从标头组件传递到主组件。因此,当我单击网格按钮时,主要内容上的视图应更改为网格视图,与行视图相同。
如何在 angular 1.5 的子组件之间传递数据?
我正在以这种方式制作一个包含 3 个子组件的组件:
<header-component>
<side-component>
<main-component>
主要组件包含英雄列表。标题组件包含两个按钮,假设将主组件上的视图切换到列表或网格视图。
我现在遇到的问题是将数据从标头组件传递到主组件。因此,当我单击网格按钮时,主要内容上的视图应更改为网格视图,与行视图相同。
如何在 angular 1.5 的子组件之间传递数据?
我建议您与 Angular 2 组件方法保持一致并使用输入/输出方法。如果这样做,您将能够轻松迁移到 Angular 2,因为组件在概念上是相同的(仅在语法上有所不同)。所以这就是你这样做的方式。
所以我们基本上希望 header 和主要组件与 header 共享状态,以便能够更改它。我们可以使用多种方法使其工作,但最简单的是利用中间父控制器属性。因此,让我们假设父控制器(或组件)定义了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
控制器可以使用类似的东西
<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>
看看和玩简单的演示。
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="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>
演示: http ://plnkr.co/edit/ODuY5Mp9HhbqA31G4w3t?p=info
这是我写的一篇博客文章,详细介绍了基于组件的设计:http: //dfsq.info/site/read/angular-components-communication
尽管父组件方法(通过属性传递数据)是一个完美有效且良好的实现,但我们可以使用存储工厂以更简单的方式实现相同的目标。
基本上,数据由 保存,Store
在两个组件范围内都引用它,从而在状态更改时启用 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>
)。
在现实世界中,一个典型的应用程序需要管理大量数据,因此以某种方式在逻辑上拆分数据域更有意义。按照相同的方法,您可以添加更多 Store 工厂。例如,要管理当前登录的用户信息以及外部资源(即目录),您可以构建一个UserStore
加CatalogStore
-UserModel
或者CatalogModel
; 这些实体也是集中与后端通信、添加自定义业务逻辑等事情的好地方。数据管理将由Store
工厂单独负责。
请记住,我们正在改变存储数据。虽然这种方法非常简单明了,但它可能无法很好地扩展,因为会产生副作用。如果您想要更高级的东西(不变性、纯函数、单状态树等),请查看Redux,或者如果您最终想切换到 Angular 2,请查看ngrx/store。
希望这可以帮助!:)
您不必以 Angular 2 的方式执行此操作,因为以防万一 您有时会迁移……如果您这样做有意义,请执行此操作。
使用自定义事件来实现这一点。您可以使用事件调度程序在您的应用程序中传递消息$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");
你可以监听你的 main-component 指令的变化,比如
$rootScope.$on("menu-changed", function(evt, arg){
console.log(arg);
});