0

I'm new to both AngularJs and ngDialog, and I'm having trouble getting my bindings to work between the ngDialog modal and my controller. I injected the controller's scope into the modal by specifying { scope: $scope }, and I have access to methods defined in the controller, but the bindings to models defined in the controller aren't functioning properly.

I'm trying to use a modal to allow the user to add an address to an organization.

Here's main.js

angular.module('wizardApp')
  .controller('MainCtrl', ['$scope', 'ngDialog', MainCtrl]);

function MainCtrl($scope, ngDialog) {
  $scope.hide_wizard_button = false;
  $scope.open_wizard = function () {
    $scope.hide_wizard_button = true;
    ngDialog.open({
      template: 'wizard',
      controller: 'wizardCtrl',
      scope: $scope
   })
 }
}

angular.module('wizardApp')
    .controller('wizardCtrl', ['$scope', wizardCtrl]);
function wizardCtrl($scope){
  $scope.step = 1;
  $scope.name = null;
  $scope.phone_number = null;
  $scope.email_address = null;
  $scope.password = null;
  $scope.step_forward = function(){
    if ($scope.step === 1){
      if(validate_name($scope.name) &&     validate_phone_number($scope.phone_number) && validate_address($scope.address)){
        $scope.step++;
      }
    }
    if ($scope.step == 2){
       if(validate_email($scope.email_address) &&   validate_password($scope.password)){
        $scope.step++;
      }
    }
  };

Following is my ng-template:

<script type="text/ng-template" id="wizard">
  <form id="msform">
    <!-- progressbar -->
    <ul id="progressbar">
      <li ng-class="{true: 'active'}[step==1]">Personal Details</li>
      <li ng-class="{true: 'active'}[step==2]">Social Profiles</li>
      <li ng-class="{true: 'active'}[step==3]">Personal Details</li>
    </ul>
    <!-- fieldsets -->
    <fieldset ng-if="step == 1">
      <h2 class="fs-title">Enter Your personal Details</h2>

      <h3 class="fs-subtitle">This is step 1</h3>
      <input type="text" placeholder="Name" ng-model="name"/>
      <input type="text" placeholder="Phone Number" ng-model="phone_number"/>
      <input type="text" placeholder="Address" ng-model="address"/>
      <input type="button" class="next action-button" value="Next" ng-click="step_forward()"/>
    </fieldset>
    <fieldset ng-if="step == 2">
      <h2 class="fs-title">Email Verification</h2>

      <h3 class="fs-subtitle">Your Email Address and password</h3>
      <input type="text" name="email" placeholder="Email Address"/>
      <input type="password" name="password" placeholder="Password"/>
      <input type="button" name="previous" class="previous action-button" value="Previous" ng-click="step_back()"/>
      <input type="button" name="next" class="next action-button" value="Next" ng-click="step_forward()"/>
    </fieldset>
    <fieldset ng-if="step == 3">
      <h2 class="fs-title">Thank You for signing up!</h2>
    </fieldset>
  </form>
</script>

The error is cannot read property of null, Which means the $scope.name is not getting updated.

Mudits
  • 1,513
  • 3
  • 20
  • 37
  • On which line do you get the "cannot read property of null" error? You set the name to null, where is it getting the new value from (from user input)? If the new value comes from the user input, does the error occur before or after the user starts typing? – Sunil D. Jun 16 '15 at 16:40
  • The error comes in `validate_name` method when I am doing `$scope.name.length` and it occurs when the next button is clicked and user has already typed the name in the textbox. – Mudits Jun 16 '15 at 16:44

1 Answers1

3

It looks like the problem is related to the ng-if directives in your template. ng-if creates a new child scope.

The new scope inherits the properties of it's parent (via prototypical inheritance). A problem like this occurs when you store your model values in primitives (strings, numbers, booleans), as opposed to objects.

Because of prototypical inheritance, the inherited string value in the child scope will shadow the value in the parent scope. E.g. you change the value in the child, but the parent doesn't notice it.

This is why the Angular developers suggest you "put a dot in your models". Store your models in an object, that can survive prototypical inheritance.

So create an object that contains all of your model values (that you're binding to in the HTML):

$scope.step1Data = { name: null, phone: null }; // etc.

Then bind to them with ng-model (using a dot!):

ng-model="step1Data.name"

Read the great explanation in this answer for the gory details.

Community
  • 1
  • 1
Sunil D.
  • 17,983
  • 6
  • 53
  • 65
  • sweet mother thank you for this! I haven't read the linked article (but I certainly will!) but does it explain how to disable/prevent the child scope from shadowing the simple property, or should we just ALWAYS use "dots in our models"? – SelAromDotNet Oct 16 '15 at 04:55
  • @SelAromDotNet Angular uses prototypical inheritance to make the child scopes, and the shadowing of properties is just how prototypical inheritance works in Javascript. The only way to disable it is to avoid creating the child scope (for example, you could create a directive to show an HTML template rather than using `ng-include` to show the same template). – Sunil D. Oct 16 '15 at 16:06
  • yep read through this last night, great (if confusing) stuff! thanks again, i feel silly being on the "yet another person stumbles on this" list but glad I finally understand it (sort of) :) – SelAromDotNet Oct 16 '15 at 16:07