Коммуникация событий от родителя к ребенку в компонентах AngularJS

в новом проекте, над которым я работаю, я начал использовать компоненты вместо директив.

однако я столкнулся с проблемой, когда я не могу найти конкретный стандартный способ сделать это.

Легко уведомить событие от дочернего к родительскому, вы можете найти его на моем plunkr ниже, но какой правильный способ уведомить событие от родителя к ребенку?

Angular2, похоже, решает эту проблему, используя что-то вроде этого: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var Но я не вижу возможности определить "указатель" на дочерний компонент, как в примере С#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 выше

Ответы

Ответ 1

Общение событий с родительского элемента на ребенка в компонентах 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"> и область определения области выделения: { localFn:'&myAttr' }, свойство scope изоляции localFn будет указывать на оболочку функции для count = count + value expression. Часто желательно передавать данные из изолированной области видимости через выражение в родительскую область. Это можно сделать, передав карту локальных имен переменных и значений в оболочку выражения fn. Например, если выражение increment($amount), то мы можем указать значение суммы, вызвав localFn как localFn({$amount: 22}).

- API-интерфейс полной архитектуры AngularJS

Как правило, я рекомендую префикс локальных переменных $ отличать их от родительских переменных.


Альтернативно используйте двунаправленную привязку

ПРИМЕЧАНИЕ. Чтобы облегчить переход на 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();
}

DEMO на PLNKR.

Ответ 2

Вот простой способ: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/

в основном, вы добавляете связанную переменную, называемую "команда" (или все, что хотите), и используйте $onChanges, чтобы обратить внимание на изменения этой переменной и вызвать любое событие, которое, по его словам, запускается вручную.

Я лично хотел бы поместить все мои переменные в объект с именем "настройки" и отправить его всем моим компонентам. Однако изменение значения внутри объекта НЕ запускает событие $onChanges, поэтому вам НЕОБХОДИМО сообщить ему, чтобы он инициировал событие с плоской переменной.

Я бы сказал, что это не "правильный" способ сделать это, но это намного проще в программировании, намного легче понять и намного проще конвертировать в A2 позже по дороге.

Ответ 3

Я столкнулся с тем же вопросом. Что вы думаете об этом подходе: использовать наследование через 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++;
        }
      }
    });

Или мы можем определить метод 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++;
    }
  }
});