Вызов метода родительского контроллера из директивы AngularJS

Следуя моему предыдущему вопросу, я сейчас пытаюсь вызвать метод на родительском контроллере из моей директивы. Я получаю неопределенный параметр. Вот что я делаю:

<body ng-app="myApp" ng-controller="MainCtrl">
  <span>{{mandat.rum}}</span>
  <span>{{mandat.surname}}</span>
<input type="text" ng-model="mandat.person.firstname" />
<my-directive mandate-person="mandat.person" updateparent="updatePerson()" >

  </my-directive>
</body>

И сценарий:

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

    app.controller('MainCtrl', function ($scope) {
        $scope.mandat = { name: "John", surname: "Doe", person: { id: 1408, firstname: "sam" } };
        $scope.updatePerson = function(person) {
            alert(person.firstname);
          $scope.mandat.person = person;   
        }
    });


    app.directive('myDirective', function () {
        return {
            restrict: 'E',
            template: "<div><span>{{mandatePerson.id}}<span><input type='text' ng-model='mandatePerson.firstname' /><button ng-click='updateparent({person: mandatePerson})'>click</button></div>",
            replace: true,
            scope: { mandatePerson: '=', updateparent: '&' }
            }
        }
    )

когда вызывается метод updatePerson, person не определен.

jsfiddle здесь: http://jsfiddle.net/graphicsxp/Z5MBf/7/

4 ответа

Решение

Просто измените HTML, как показано ниже

<my-directive mandate-person="mandat.person" updateparent="updatePerson(person)" >

      </my-directive>

вы не передаете "лицо" с updatePerson, поэтому оно не работает

Доступ к методу контроллера означает доступ к методу в родительской области действия из директивы controller / link / scope.

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

Немного больше работы требуется, если вы хотите получить доступ к методу родительской области действия из области действия изолированной директивы.

Существует несколько опций (может быть больше перечисленных ниже) для вызова метода родительской области из изолированной области действия директив или просмотра переменных родительской области (специально для опции № 6).

Обратите внимание, что я использовал link function в этих примерах, но вы можете использовать directive controller а также на основе требований.

Опция 1. Через литерал объекта и из директивы html шаблона

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChanged({selectedItems:selectedItems})" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]

});

рабочий plnkr: http://plnkr.co/edit/rgKUsYGDo9O3tewL6xgr?p=preview

Вариант № 2. Через литерал объекта и из директивы link / scope

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html",
    link: function (scope, element, attrs){
      scope.selectedItemsChangedDir = function(){
        scope.selectedItemsChanged({selectedItems:scope.selectedItems});  
      }
    }
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

рабочий plnkr: http://plnkr.co/edit/BRvYm2SpSpBK9uxNIcTa?p=preview

Вариант № 3. Через ссылку на функцию и из директивы html шаблона

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-change="selectedItemsChanged()(selectedItems)" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems:'=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

рабочий plnkr: http://plnkr.co/edit/Jo6FcYfVXCCg3vH42BIz?p=preview

Вариант № 4. Через ссылку на функцию и из директивы link / scope

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html",
    link: function (scope, element, attrs){
      scope.selectedItemsChangedDir = function(){
        scope.selectedItemsChanged()(scope.selectedItems);  
      }
    }
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]

});

работает plnkr: http://plnkr.co/edit/BSqx2J1yCY86IJwAnQF1?p=preview

Вариант № 5: с помощью ng-модели и двухсторонней привязки вы можете обновить родительские переменные области видимости., Поэтому в некоторых случаях вам может не потребоваться вызывать родительские функции области видимости.

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter ng-model="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=ngModel'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

рабочий plnkr: http://plnkr.co/edit/hNui3xgzdTnfcdzljihY?p=preview

Вариант № 6: Через $watch а также $watchCollection Это двухстороннее связывание для items во всех приведенных выше примерах, если элементы изменяются в родительской области, элементы в директиве также отражают изменения.

Если вы хотите посмотреть другие атрибуты или объекты из родительской области, вы можете сделать это, используя $watch а также $watchCollection как указано ниже

HTML

<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <p>Hello {{user}}!</p>
  <p>directive is watching name and current item</p>
  <table>
    <tr>
      <td>Id:</td>
      <td>
        <input type="text" ng-model="id" />
      </td>
    </tr>
    <tr>
      <td>Name:</td>
      <td>
        <input type="text" ng-model="name" />
      </td>
    </tr>
    <tr>
      <td>Model:</td>
      <td>
        <input type="text" ng-model="model" />
      </td>
    </tr>
  </table>

  <button style="margin-left:50px" type="buttun" ng-click="addItem()">Add Item</button>

  <p>Directive Contents</p>
  <sd-items-filter ng-model="selectedItems" current-item="currentItem" name="{{name}}" selected-items-changed="selectedItemsChanged" items="items"></sd-items-filter>

  <P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}}</p>
</body>

</html>

скрипт app.js

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

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      name: '@',
      currentItem: '=',
      items: '=',
      selectedItems: '=ngModel'
    },
    template: '<select ng-model="selectedItems" multiple="multiple" style="height: 140px; width: 250px;"' +
      'ng-options="item.id as item.name group by item.model for item in items | orderBy:\'name\'">' +
      '<option>--</option> </select>',
    link: function(scope, element, attrs) {
      scope.$watchCollection('currentItem', function() {
        console.log(JSON.stringify(scope.currentItem));
      });
      scope.$watch('name', function() {
        console.log(JSON.stringify(scope.name));
      });
    }
  }
})

 app.controller('MainCtrl', function($scope) {
  $scope.user = 'World';

  $scope.addItem = function() {
    $scope.items.push({
      id: $scope.id,
      name: $scope.name,
      model: $scope.model
    });
    $scope.currentItem = {};
    $scope.currentItem.id = $scope.id;
    $scope.currentItem.name = $scope.name;
    $scope.currentItem.model = $scope.model;
  }

  $scope.selectedItems = ["allItems"];

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
  }]
});

Вы всегда можете обратиться к документации AngularJs для подробного объяснения директив.

Есть два способа, которыми мы можем позвонить используя & а также =,

Если я использую = для атрибута области, то

ng-click='updateparent({person: mandatePerson})' 

будет изменен на

ng-click='updateparent(mandatePerson)'

И в директиве,

updateparent="updatePerson()" 

изменится на

updateparent="updatePerson"

Здесь нет необходимости упоминать аргументы, они будут переданы в определение функции контроллера в качестве ссылки.

С помощью & объясняется в других ответах.

Вот еще один паттерн (работает в Angular 1.5).

angular.module('module', [])

    .controller('MyController', function() {

        var self = this;
        self.msg = 0;
        // implement directive event listener interface
        this.onEvent = function(arg) {
            self.msg++;
        };
    })

    .directive('myDirective', function() {
        return {
          scope: {
            data: '=',
            handler: '='
          },
          template: '<button ng-click="handler.onEvent(data)">Emit event</button>'
        }
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>


<div ng-app="module" ng-controller="MyController as ctrl">
<my-directive handler="ctrl" data="'...received'"></my-directive>
  {{ctrl.msg}}
</div>

Другие вопросы по тегам