Search code examples
angularjsangularjs-directiveangularjs-scope

Returning data from directive template component to global $scope


What I want to achieve is to create a component consisting of a dropdown list of dynamically populated checkboxes. - DONE

These checkboxes should fetch the names for the values inside the directive from an attribute property-name of a directive element. - DONE

They need to return the values (which of them are selected) to a variable in $scope which name is the same as the attribute my-model so I could read it from the main page referencing to $scope.firstTitle and $scope.secondTitle as in plunker. - This is my problem

The most important part is that the solution should allow to insert many components on the same page without the variable in $scope of the previous component be overwritten by next one.

I've tried searching for an answer but none seem to address my exact issue which is populating values from one source to a template and passing the values from template to $scope.

Here's a plunker of what I've already achieved: https://plnkr.co/edit/Uwarzh7qxrE3pDKxH2u8?p=preview

HTML:

   <div ng-app="testModule" ng-controller="componentsController">

      <div class="container col-md-3">
        <mydropdown property-name="First Title" my-model="firstTitle"></mydropdown>
      </div>

      <div class="container col-md-3">
        <mydropdown property-name="Second Title" my-model="secondTitle"></mydropdown>
      </div>

      <button class="btn btn-success" type="submit" value="Save" ng-click="testSave()">Test data output</button>
    </div>

JS:

angular.module('testModule', [])
    .controller('componentsController', ["$scope", function ($scope) {
        $scope.testSave = function() {
            console.log($scope);
        }

    }])
    .directive('mydropdown', function () {
        return {
            scope: {
                propertyName: '@',
                myModel:'='
            },
            templateUrl: 'component.html',

            link: function (scope) {

              // this will be replaced with call to service
                scope.dropDowns = [
                    {
                        value: 'test1',
                        text: 'Test1'
                    },
                    {
                        value: 'test2',
                        text: 'Test2'
                    }
                ];
            }
        }
    });

Component.html:

<div class="dropdown">
    <button class="btn btn-block btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        <span class="pull-left">{{propertyName}}</span>
        <i class="pull-right glyphicon glyphicon-triangle-bottom"></i>
    </button>
    <ul class="dropdown-menu">
        <li ng-repeat="dropDown in dropDowns">
            <div class="checkbox col-md-12">
                <label>
                    <input type="checkbox" ng-model="dropDown.value"> {{dropDown.text}}
                </label>
            </div>
        </li>
    </ul>
</div>

Please help.


Solution

  • Put the model in the main controller:

    app.controller("mainCtrl", function() {
      var vm = this;
      vm.firstDropdowns = [
        {
          value: 'test1',
          text: 'Test1'
        },
       {
          value: 'test2',
          text: 'Test2'
        }
      ];
    })
    

    Connect with the component in the template:

       <mydropdown  my-model="vm.firstDropdowns">
       </mydropdown>
    

    Use one-way & binding in the component:

    app.component('mydropdown', {
        bindings: {
            myModel: '<',
        },
        template: `
            <div ng-repeat="dropDown in $ctrl.myModel">
                <input type="checkbox" ng-model="dropDown.value" />
                {{dropDown.text}}
            </div>
        `
    })
    

    Because the input is an object, the ng-model directives inside the component will update the contents of the object and changes will be seen by the parent controller.


    The DEMO

    angular.module("app",[])
    
    .controller("mainCtrl", function() {
      var vm = this;
      vm.firstDropdowns = [
        {
          value: 'test1',
          text: 'Test1'
        },
       {
          value: 'test2',
          text: 'Test2'
        }
      ];
    })
    
    .component('mydropdown', {
        bindings: {
            myModel: '<',
        },
        template: `
            <div ng-repeat="dropDown in $ctrl.myModel">
                <input type="checkbox" ng-model="dropDown.value" />
                {{dropDown.text}}
            </div>
        `
    })
      <script src="//unpkg.com/angular/angular.js"></script>
      <body ng-app="app" ng-controller="mainCtrl as vm">
        <fieldset>
           <mydropdown  my-model="vm.firstDropdowns">
           </mydropdown>
        </fieldset>
        <fieldset>
          <div ng-repeat="x in vm.firstDropdowns">
            {{x.value}} {{x.text}}
          </div>
        </fieldset>
      </body>