24

在我正在处理的新项目中,我已经开始使用组件而不是指令。

但是,我遇到了一个问题,我找不到具体的标准方法来做到这一点。

将事件从孩子通知给父母很容易,您可以在下面的我的 plunkr 上找到它,但是将事件从父母通知给孩子的正确方法是什么?

Angular2 似乎通过使用这样的东西来解决这个问题:https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var 但我没有tink 可以定义一个指向子组件的“指针”,就像示例中使用#timer 所做的那样

为了保持可能轻松转换为 Angular2,我想避免:

  • 事件发射(从范围发射和广播)
  • 使用来自孩子的要求(然后向父母添加回调..UGLY)
  • 使用单向绑定,将作用域注入子级,然后“监视”此属性.. 更丑陋

示例代码:

var app = angular.module('plunker', []);

app.controller('RootController', function() {
});

app.component('parentComponent', {
  template: `
    <h3>Parent component</h3>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Child</a>
    <span data-ng-bind="$ctrl.childMessage"></span>
    <child-component on-change="$ctrl.notifiedFromChild(count)"></child-component>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.notifiedFromChild = function(count){
      ctrl.childMessage = "From child " + count;
    }
    ctrl.click = function(){
    }
  },
  bindings: {
  }
});

app.component('childComponent', {
  template: `
    <h4>Child component</h4>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Parent</a>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.counter = 0;
    ctrl.click = function(){
        ctrl.onChange({ count: ++ctrl.counter });
    }
  },
  bindings: {
    onChange: '&'
  }
});

你可以在这里找到一个例子:

http://plnkr.co/edit/SCK8XlYoYCRceCP7q2Rn?p=preview

这是我创建的一个可能的解决方案

http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview

孩子需要父母的地方,然后孩子设置了对孩子的父母引用......现在父母可以使用孩子......丑陋但就像上面的angular2示例

4

4 回答 4

31

在 AngularJS 组件中从父级到子级通信事件

使用表达式绑定发布指令 $API

要允许父组件将事件传递给子组件,请让子组件发布 API:

<grid-component grid-on-init="$ctrl.gridApi=$API; $ctrl.someFn($API)">
</grid-component>    

JS

app.component('gridComponent', {
  //Create API binding
  bindings: {gridOnInit: "&"},
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}</p>
  `,
  controller: function() {
    var ctrl = this;
    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        //Publish save function
        ctrl.api.save = save;
        //Invoke Expression with $API as local
        ctrl.gridOnInit({$API: ctrl.api});
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});

上面的示例调用由属性定义的 Angular 表达式grid-on-init,其 API 公开为$API. 这种方法的优点是父母可以通过使用 Angular 表达式将函数传递给子组件来对子初始化做出反应。

从文档:

“隔离”范围对象哈希定义了一组从指令元素的属性派生的本地范围属性。这些本地属性对于模板的别名值很有用。对象哈希中的键映射到隔离范围上的属性名称;这些值通过指令元素上的匹配属性定义属性如何绑定到父范围:

  • &&attr- 提供一种在父作用域的上下文中执行表达式的方法。如果未指定 attr 名称,则假定属性名称与本地名称相同。给定<my-component my-attr="count = count + value">和隔离范围定义 scope: { localFn:'&myAttr' },隔离范围属性localFn将指向count = count + value expression. 通常希望通过表达式将数据从隔离范围传递到父范围。这可以通过将局部变量名称和值的映射传递到表达式包装器 fn 来完成。例如,如果表达式是,那么我们可以通过调用asincrement($amount)来指定金额值。localFnlocalFn({$amount: 22})

-- AngularJS 综合指令 API -- 范围

作为惯例,我建议在局部变量前面加上前缀$以将它们与父变量区分开来。


交替使用双向绑定

注意:为了便于过渡到 Angular 2+,请避免使用双向=绑定。而是使用单向<绑定和表达式&绑定。有关更多信息,请参阅AngularJS 开发人员指南 - 了解组件

要允许父组件将事件传递给子组件,请让子组件发布 API:

<grid-component api="$ctrl.gridApi"></grid-component>

在上面的示例中,grid-component使用绑定将其 API 发布到使用api属性的父范围。

app.component('gridComponent', {
  //Create API binding
  bindings: {api: "="},
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}</p>
  `,
  controller: function() {
    var ctrl = this;
    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        //Publish save function
        ctrl.api.save = save;
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});

然后父组件可以save使用发布的 API 调用子函数:

ctrl.click = function(){
  console.log("Search clicked");
  ctrl.gridApi.save();
}

PLNKR 上的演示。

于 2016-05-25T23:31:32.767 回答
5

这是一个简单的方法: http: //morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/

基本上,您添加一个名为“command”(或任何您想要的)的绑定变量,并使用 $onChanges 来注意该变量的更改并触发它所说的手动触发的任何事件。

我个人喜欢将所有变量放入一个名为“设置”的对象中,并将其发送给我的所有组件。但是,对象中值的更改不会触发 $onChanges 事件,因此您需要告诉它使用平面变量触发事件。

我想说这不是“正确”的做法,但它确实更容易编程,更容易理解,并且以后更容易转换为 A2。

于 2017-03-31T21:40:03.370 回答
2

我面临同样的问题。您如何看待这种方法:使用继承require而不是双向绑定?

http://plnkr.co/edit/fD1qho3eoLoEnlvMzzbw?p=preview

var app = angular.module('plunker', []);

    app.controller('RootController', function() {
    });

    app.component('filterComponent', {
      template: `
        <h3>Filter component</h3>
        <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Search</a>
        <span data-ng-bind="$ctrl.childMessage"></span>

        <grid-component api="$ctrl.gridApi"></grid-component>
      `,
      controller: function() {
        var ctrl = this;

        ctrl.click = function(){
          console.log("Search clicked");
          ctrl.gridApi.save();
        };
      }
    });

    app.component('gridComponent', {
      require: {parent:'^^filterComponent'},
      bindings: {api: "<"},
      template: `
        <h4>Grid component</h4>
        <p> Save count = {{$ctrl.count}}
      `,
      controller: function() {
        var ctrl = this;



        this.$onInit = function() {
            ctrl.count = 0;
            ctrl.api = {};
            ctrl.api.save = save;

            ctrl.parent.gridApi = ctrl.api;
        };
        function save(){
          console.log("saved!");
          ctrl.count++;
        }
      }
    });

或者我们可以为 parent定义 setter 方法以使其更明确。

http://plnkr.co/edit/jmETwGt32BIn3Tl0yDzY?p=preview

var app = angular.module('plunker', []);

app.controller('RootController', function() {
});

app.component('filterComponent', {
  template: `
    <h3>Filter component</h3>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Search</a>
    <span data-ng-bind="$ctrl.childMessage"></span>

    <grid-component pass-api="$ctrl.setGridApi(api)"></grid-component>
  `,
  controller: function() {
    var ctrl = this;

    var gridApi = {};

    ctrl.setGridApi = function(api){
      gridApi = api;
    };

    ctrl.click = function(){
      console.log("Search clicked");
      gridApi.save();
    };
  }
});

app.component('gridComponent', {
  bindings: {
    passApi:'&'
  },
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}
  `,
  controller: function() {
    var ctrl = this;

    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        ctrl.api.save = save;

        ctrl.passApi({api: ctrl.api});
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});
于 2017-11-09T15:19:26.247 回答
0

简单:您只需要一个属性 1 方式绑定,因为 2 方式绑定仅在创建时调用 onChanges。

  1. 在父控制器上设置一个新的布尔属性。

    vm.changeNow = 假;//当你想告诉组件调用方法时,将其更新为 vm.changeNow = !vm.changeNow。

  2. 打开子组件,在绑定部分,

    绑定:{ a2waybind:'=',changenow:'<'}

  3. 您现在需要对孩子进行 $onChanges 事件。

    $onChanges() { // 做那些你想从父母那里听的甜蜜的事情。}

  4. 现在调用模板时:

    childComponent a2waybind="$ctrl.mycoolvalue" changenow="$ctrl.changeNow" /childComponent"

第二种方法是在您的子组件中:

        var vm = this;
        var myprop;
        Object.defineProperty(vm, 'mytwowayprop', {
            get() {
                return myprop;
            },
            set(value) {
                myprop = value; 
                vm.onchangeseventbecausemypropchanged();               
            }
        });
       vm.onchangeseventbecausemypropchanged = function () {//woot}

当您的双向绑定属性在内部和外部发生变化时,这将允许您定义 onchanges 事件。

于 2019-12-14T00:16:39.740 回答