1

Note: I've reproduced my code here in JS Bin

I have a directive, that prints out an array of items to a web page. Suppose that the line 'console.log("added")' was actually complex logic, and that it was something we wanted the directive to do every time, regardless of the controller using it. It could make an AJX call to indicate, a new record have been added.

So I'm imagining there would be a function in the directive that does this. And instead of using "$scope.items" to add a new item, the controller would call this function in the directive. Unless there's a better way to do this, can you tell me how to approach this?

HTML:

<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body ng-app="app">
  <div ng-controller="MyCtrl">
    <input type="text" ng-model="newitem.name"/>
    <button type="button" ng-click="addNewItem(newitem)">Add Item</button>
    <menu items="items"/>
  </div>
</body>
</html>

JavaScript:

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

app.controller('MyCtrl', function($scope) {
  $scope.items = [
    {name: "item1"},
    {name: "item2"},
    {name: "item3"},
    {name: "item4"}
  ];

  $scope.addNewItem = function(newItem) {
    $scope.items.push(angular.copy(newItem));
    console.log("added");  //PRETEND THIS IS COMPLEX LOGIC WE WANT MOVED TO DIRECTIVE
  };
});

app.directive("menu", function(){
  return {
    restrict: "E",
    scope: {
      items: "=" 
    },
    template: "<div ng-repeat='item in items'>{{item.name}}</div>"
  };
});

1 Answer 1

2

Usually you make ajax requests through services.

Let's say you want to save the added item to a database through REST api and then recieve the response and parse it to the menu list, then you'll do something like this:

app.service('MenuService', function($http) {
  this.addItem = function(item) {
    return $http.post('/rest/menu', {menuItem: item});
  }
});

Back in your controller:

app.controller('MyCtrl', function($scope, MenuService) {
  $scope.items = [
    {name: "item1"},
    {name: "item2"},
    {name: "item3"},
    {name: "item4"}
  ];

  $scope.addNewItem = function(newItem) {
    MenuService.addItem(newItem).then(function(itemFetchedFromWebService) {
      $scope.items.push(itemFetchedFromWebService);
    }, function(error) {console.log(error); });
  };
});

If you don't need to manipulate the dom more than the ng-repeat directive already does, and you also don't need to recieve additional data through attributes, then you don't really need the directive here. the controller should do the work already.

Edit

If you prefer to use the directive, you can use the link function:

app.directive("menu", function(MenuService){ //Note the added dependency
  return {
    restrict: "E",
    scope: {
      items: "=" 
    },
    template: "<div ng-repeat='item in items'>{{item.name}}</div>",
    link: function(scope) {
      scope.addNewItem = function(newItem) {
       MenuService.addItem(newItem).then(function(itemFetchedFromWebService){
        scope.items.push(itemFetchedFromWebService);
       }, function(error) {console.log(error); });
      };
    }
  };
});

What the link function expose for you to use:

  • Current scope object (The isolated scope in your case).
  • The element of that directive (wrapped in angular.element object), you can access the dom element with "element[0]"
  • Object that hold the attributes of the element.

There are 2 more arguments you can use in your link function: (Out of the scope of the question)

The fourth argument let you use the controllers of other diretive in the current element or the parent/sibling elements.

The fifth argument is the transclude function that allows you to manipulate the transcluded elements.

Sign up to request clarification or add additional context in comments.

5 Comments

The only issue I have with this solution is that the logic is still tied to the controller instead of the directive, and I'm planning on having multiple controllers use this directive. So I'd have to make sure none of these controllers forget to use "MenuService.addItem". I'm imagining a directive that can expose an API that could be called. But your solution will work alright, and if I can't find anything better, I'll certainly use it. (Also note that my directive is more complex then this, but I didn't want it to confuse the question.)
A. You can bind the controller to your directive by controller: 'YourCtrl' in the directive object. B. You can do the same thing in the link function, updated the answer so you can view the use of the directive in your case
Perfect, but how do you call the link function's addNewItem function from the controller?
You can't, unless you bind to the controller as i mentioned before. If you do, it should be available on the scope

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.