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

Bes*_* N. 48 components angularjs angularjs-controlleras

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

<header-component>
<side-component>
<main-component>
Run Code Online (Sandbox Code Playgroud)

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

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

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

dfs*_*fsq 83

组件方法

我建议你与Angular 2组件方法一致并使用输入/输出方法.如果这样做,您将能够轻松迁移到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: '&'
  }
})
Run Code Online (Sandbox Code Playgroud)

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

所以这意味着header控制器可以使用类似的东西

<header-component view="root.view" on-view-change="root.view = $event.view"></header-component> 
Run Code Online (Sandbox Code Playgroud)

主要组成部分:输入.

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

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

父视图

最后它们连在一起:

<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
<main-component view="root.view"></main-component>
Run Code Online (Sandbox Code Playgroud)

看看并玩简单的演示.

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: '<'
  }
})
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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


这是我写的一篇博文,详细介绍了基于组件的设计:http://dfsq.info/site/read/angular-components-communication

  • @BesaNeziri您不必为所有内容引入输入/输出属性.如果它们在逻辑上属于同一个东西,你可以创建单个属性,比如`Input()config`,它将是一个像`config.sorting`,`config.pagination`,`config.filter`等对象.所以它更少乱.同样对于复杂的情况,使用通用服务(如两个组件中的"GridService")来处理这些事情是有意义的. (2认同)

And*_*ddu 15

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

基本上,数据由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);
            },
        };
    });
Run Code Online (Sandbox Code Playgroud)

然后,在您的组件中,您应该具有以下内容:

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>
            ...
        `
Run Code Online (Sandbox Code Playgroud)

如果您想查看一个有效的示例,请查看此CodePen.

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

  1. 注入商店依赖
  2. 确保将商店数据链接到组件范围

比如上面的例子(<header-component>).

在现实世界中,典型的应用程序需要管理大量数据,因此以某种方式逻辑分割数据域更有意义.按照相同的方法,您可以添加更多Store工厂.例如,要管理当前登录的用户信息以及外部资源(即目录),你可以建立一个UserStore加一个CatalogStore-或者UserModelCatalogModel; 这些实体也是集中管理后端,添加自定义业务逻辑等内容的好地方.数据管理将由Store工厂全权负责.

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

希望这可以帮助!:)

你没有必要采用Angular 2方式,因为万一 你有时会迁移......如果你有意义的话就去做吧.


Sub*_*raj 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");
Run Code Online (Sandbox Code Playgroud)

你可以听一下主要组件指令的变化

$rootScope.$on("menu-changed", function(evt, arg){
  console.log(arg);
});
Run Code Online (Sandbox Code Playgroud)

  • 这是不好的做法.您应该尝试遵循组件文档https://docs.angularjs.org/guide/component中描述的方法 (2认同)