Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper method to handle Angular scope instead of $parent.$parent

I've been reading a few articles, and haven't found the example that solves my issue.

My understanding is that:

  1. ng-if and ng-repeat create isolate scopes.
  2. Using $parent.someProperty is bad.
  3. Using $parent.$parent.someProperty would be an abomination.

So, with the given template markup, how can I properly bind the the controller so that the controller property updates?

Markup:
(note the nested ng-if and ng-repeat, creating a sort of $parent.$parent situation)

<div ng-app="MyApp" ng-controller="MyCtrl">
  <div ng-if="showOnCondition">
    <label ng-repeat="item in repeatingItems">
      {{item}}
      <setting item="item" />
    </label>
  </div>
  {{checkSetting()}}
</div>

JavaScript

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

myApp.controller('MyCtrl', function($scope) {
  $scope.settings = {
    someProperty: '',
    anotherProperty: 'Hello'
  }

  $scope.repeatingItems = [
    'One',
    'Two',
    'Three'
  ];

  $scope.showOnCondition = true;

  $scope.checkSetting = function() {
    // When calling checkSettings(), I would like to access the value of someProperty here
    return $scope.settings;
  }
});

myApp.directive('setting', function() {
  return {
    restrict: 'E',
    require: '^myCtrl',
    // HOW DO I SOLVE THIS?
    // $parent.$parent is bad.
    template: '<input type="radio" ng-model="$parent.$parent.settings.someProperty" name="mySetting" value="{{item}}" />',
    scope: {
      settings: '=',
      item: '='
    }
  };
});

Given the above example, how to I properly construct the directive and / or markup in order to access settings.someProperty in the controller? Or is there something entirely different I need to be doing?

Clarification
There seems to be some confusion around what I'm trying to do. The someProperty is available in the directive - that is working fine. Please note I'm trying to assign values to the controller's someProperty property from within the directive (using ng-model)

Update
I've revised the code above to known, working code, plus added a jsFiddle. Note that it works, but it uses $parent.$parent in the template. This is the problem I need to understand how to solve.

like image 621
random_user_name Avatar asked Oct 19 '22 17:10

random_user_name


2 Answers

You can pass settings through to the directive, which I think is the simplest / most direct / cleanest way to do this.

Angular properly evaluates settings as MyCtrl.settings, then passes it through to the isolate scope of the setting directive. Inside the directive, you have two-way binding on settings, so you can update settings from there.

Modifying your example, pass in settings into the directive in the Markup:

<setting settings="settings" item="item" />

Then in the JavaScript, swap out this for the template:

template: '<input type="radio" ng-model="settings.someProperty" ng-value="item" name="mySetting" />',

Here's a working fiddle.

like image 153
ryanm Avatar answered Oct 21 '22 06:10

ryanm


EDIT

I'm rewriting my answer completely, based on your fiddle and a simpler two-way data-binding approach. Note to future readers that the first couple of comments are no longer relevant to this updated answer.

HTML

<div ng-app="MyApp" ng-controller="MyCtrl">
  <div ng-if="showOnCondition">
    <label ng-repeat="item in repeatingItems">
      {{item}}
      <setting test="settings.someProperty" item="item" />
    </label>
  </div>
  {{checkSetting()}}
</div>

JS

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

myApp.controller('MyCtrl', function($scope) {
  $scope.settings = {
    someProperty: true,
    anotherProperty: 'Hello'
  }

  $scope.repeatingItems = [
    'One',
    'Two',
    'Three'
  ];

  $scope.showOnCondition = true;

  $scope.checkSetting = function() {
    // When calling checkSettings(), I would like to access the value of someProperty here
    return $scope.settings;
  }
});

myApp.directive('setting', function() {
  return {
    restrict: 'E',
        replace: true,
    template: '<input type="radio" ng-model="test" name="mySetting" value="{{item}}" />',
    scope: {
      test: '=',
      item: '='
    }
  };
});

This provides two-way data-binding between the parent controller property and the directive. With this approach, you would pass in each individual parent scope property to an attribute, and bind it in your isolate scope.

like image 43
Daniel Nalbach Avatar answered Oct 21 '22 06:10

Daniel Nalbach