Dismiss
Announcing Stack Overflow Documentation

We started with Q&A. Technical documentation is next, and we need your help.

Whether you're a beginner or an experienced developer, you can contribute.

Sign up and start helping → Learn more about Documentation →

I have a pretty simple itemController which contains an array of items (in actuality, these are obtained from an API). I want to then call the API again to get the subsections based on what is selected from the itemForm.item select (i.e., get all subsections for itemForm.item.id).

When I select Item1 (id=1), I want to update the form to include the subsections for that item.id. The problem I am having is how exactly to pass the itemForm.item.id into the sectionController. As you can see below, I tried it with $attrs, but this ends up passing in a string of "{{itemForm.item.id}}" instead of the actual value.

I am new to Angular and am quite possibly going about this wholly the wrong way, so please let me know if I am totally off base in my approach.

Javascript

angular.module('app').controller('itemController', function(){
this.items = [
  {
   "id": 1, 
   "name": "Item1"
  }, 
  {
   "id": 2, 
   "name": "Item2"
  }, 
  {
   "id": 3, 
   "name": "Item3"
  }
];
}];

angular.module('app').controller('sectionController', 
['SectionService', '$attrs',
function(SectionService, $attrs){
var store = this;

SectionService.getSections($attrs.id)
    .then(function(data) {
        store.sections = data;
    }, function(error) {
        // promise rejected
        console.log(error);
    });

}]);

HTML

<div class="form-group" ng-controller="itemController as item">
    <select ng-model="itemForm.item" ng-options="item as item.name for item in item.items track by item.id" required>
    </select>

    <!-- this is just a test to try to get the behavior to work correctly -->
    <div ng-controller="sectionController as section" id="{{$itemForm.item}}">
        <p ng-repeat="section in section.sections">{{section.name}}</p>
    </div>
</div>

The SectionService is a factory which simply calls the API and returns a JSON array such as this for a particular itemID (itemID=1 in this case):

[
  {
    "name": "Section1", 
    "itemID": 1, 
    "sectionID": 1
  }, 
  {
    "name": "Section2", 
    "itemID": 1, 
    "sectionID": 2
  }, 
  {
    "name": "Section3", 
    "itemID": 1, 
    "sectionID": 3
  }, 
  {
    "name": "Section4", 
    "itemID": 1, 
    "sectionID": 4
  }
]
share|improve this question

SectionService is only getting called once, when sectionController is being loaded for the first time.

You need to use $scope.$watch to observe changes to $attrs.id if you want it to call SectionService every time the value for itemForm.item changes:

angular.module('app').controller('sectionController', 
['SectionService', '$attrs', '$scope',
function(SectionService, $attrs, $scope){
var store = this;

$scope.$watch(
  function(){ return $attrs.id; },
  function(){
     if($attrs.id){
        SectionService.getSections($attrs.id)
           .then(function(data) {
               store.sections = data;
        }, function(error) {
           // promise rejected
           console.log(error);
        });
     }
  }
}]);
share|improve this answer

First of all, you must not use as expression with the track by, you should use only one of them.

From docs:

Be careful when using select as and track by in the same expression.

I'd suggest you to use like this:

<select class="form-control" ng-model="itemForm.item" ng-options="item.name for item in item.items track by item.id" ng-change="item.change(itemForm.item.id)" required>

Well, to achieve what you want, you have to do some things:

  1. Use ngChange directive to get the id of the item selected in your parent controller:
ng-change="item.change(itemForm.item.id)"
  1. Inject the $scope in both controllers;

  2. Use the $broadcast to send data to the child controller

$scope.$broadcast('id', id);
  1. Use $on to receive the data from the parent controller:
$scope.$on('id', function(event, id) {
  ...
});

Here's a complete example:

(function() {
  'use strict';

  angular
  .module('app', [])
  .controller('itemController', itemController)
  .controller('sectionController', sectionController)
  .factory('SectionService', SectionService);

  sectionController.$inject = ['$scope'];
  
  function itemController($scope) {
    var vm = this;
    vm.change = change;
    vm.items = [  
       {  
          "id":1,
          "name":"Item1"
       },
       {  
          "id":2,
          "name":"Item2"
       },
       {  
          "id":3,
          "name":"Item3"
       }
    ];
  
    function change(id) {
      // send data to child controller
      $scope.$broadcast('id', id);
    }
  }
  
  sectionController.$inject = ['SectionService', '$scope'];
  
  function sectionController(SectionService, $scope) {
    var store = this;
    var id = 1;
    // It's only to work here...
    var fakeData = [  
       {  
          "name":"Section1",
          "itemID":1,
          "sectionID":1
       },
       {  
          "name":"Section2",
          "itemID":1,
          "sectionID":2
       },
       {  
          "name":"Section3",
          "itemID":1,
          "sectionID":3
       },
       {  
          "name":"Section4",
          "itemID":1,
          "sectionID":4
       }
    ];
    
    function getSuccess(response) {
      // store.sections = response.data; <- it should be uncommented in your real application
      store.sections = fakeData;
    }
  
    function getError(response) {
      console.log(response);
    }
  
    // get the broadcast
    $scope.$on('id', function(event, id) {
      if (!id) return;
      getSuccess();
      /*SectionService.getSections(id)
        .then(getSuccess)
        .catch(getError); <- it should be uncommented in your real application */
    })
  }
  
  SectionService.$inject = ['$http'];
  
  function SectionService($http) {
    var factory = {
      getSections: getSections
    }
  
    return factory;
  
    function getSections(id) {
      //return $http.get('url' + id); <- it should be uncommented in your real application
    }
  }
})();
<!DOCTYPE html>
<html ng-app="app">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
</head>

<body>
  <div class="form-group" ng-controller="itemController as item">
    <select class="form-control" ng-model="itemForm.item" ng-options="item.name for item in item.items track by item.id" ng-change="item.change(itemForm.item.id)" required>
      <option value="" label="Select item" hidden>
    </select>
    <hr>
    <div ng-controller="sectionController as section">
      <p ng-repeat="section in section.sections" ng-bind="section.name"></p>
    </div>
  </div>
</body>

</html>

Please, note that I comment the parts that couldn't work here in snippet, but it should work in your real application.

I'd also recommend you to check this tutorial.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.