user979331
user979331

Reputation: 11861

Angular JS controller element is undefined when called in directive

I have a controller like so:

myApp.controller('WizardController', function ($scope, $http) {

$scope.user = {
    addressline1: null,
    cobuyer: null,
    validate: null,
    cobuyerfirstname: null,
    cobuyerlastname: null,
    cobuyeremail: null,
    cobuyerdob: null,
    cobuyeraddressline1: null,
    cobuyeraddressline2: null,
    cobuyercity: null,
    cobuyerprovince: null,
    cobuyerpostalcode: null,
    cobuyerphone: null,
    dobString: null,
    cobuyerDobString: null,
    isWaitList: false
};

});

and I add this directive below:

myApp.directive('checkbox', function () {
    return {
        restrict: 'A',
        replace: false,
        transclude: true,
        scope: {
            user: '='
        },
        template: '<label for="addressSame" class="form-label col-md-2" style="width: auto;">Same as Buyers Address</label><input type="checkbox" name="addressSame" ng-click="isSameAddress()" ng-model="isChecked" />',
        link: function (scope, element, attrs) {
            // Initialize to unchecked
            scope.isChecked = false;

        },
        controller: function ($scope) {

            $scope.isSameAddress = function () {

                console.log($scope.isChecked);

                if ($scope.isChecked == true) {
                    console.log($scope.user);
                }
                else {

                }
            }

        }
    }
});

Now my checkbox appears as it should, I am able to check it and get the true or false from scope.isChecked. I am now trying to get items from $scope.user, but it says its undefined. I am fairly new to Angular JS and I have no idea how to fix this...How do i get an element that is defined in the controller?

I have also tried this, still undefined:

myApp.directive('checkbox', function () {
    return {
        restrict: 'A',
        replace: false,
        transclude: true,
        scope: {
            user: '='
        },
        template: '<label for="addressSame" class="form-label col-md-2" style="width: auto;">Same as Buyers Address</label><input type="checkbox" name="addressSame" ng-click="isSameAddress()" ng-model="isChecked" />',
        link: function (scope, element, attrs) {
            // Initialize to unchecked
            scope.isChecked = false;

        },
        controller: function ($scope, $http, $rootScope) {

            $scope.isSameAddress = function () {

                console.log($scope.isChecked);

                if ($scope.isChecked == true) {
                    console.log($rootScope.user);
                }
                else {

                }
            }

        }
    }
});

Upvotes: 0

Views: 1012

Answers (4)

Intervalia
Intervalia

Reputation: 10945

Since you didn't provide any other code it is hard to know exactly what you are doing. So I made some assumptions:

  • I assumed that your controller WizardController was an outer controller.
  • I assume that you really were not using transclusion since your template didn't use it. So I removed transclude: true from the directive.
  • I formatted the code to fit my style. I find is easier to break out my functions from the angular registration code. and group things together. I also added code to handle minification if you were planning to do that.
  • I never embed my controller code within my directives. This fits the Single Responsibility Principle a little better.

I will give more detail below.

Here is my code:

<!DOCTYPE html>
<html ng-app="myApp">
  <head>
    <title>AngularJS Example</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
    <script>
    // Controller code for application
    AppController.$inject = ['$scope', '$http']; // <- This is needed only if you plan to minify your JS code.
    function AppController($scope, $http) {
      $scope.user = {
        addressline1: '', // <-- Since these values will display in the HTML use an empty string instead of null
        cobuyer: '',
        validate: '',
        cobuyerfirstname: 'John',
        cobuyerlastname: 'Smith',
        cobuyeremail: '[email protected]',
        cobuyerdob: '',
        cobuyeraddressline1: '123 Some Street',
        cobuyeraddressline2: '',
        cobuyercity: 'Anchorage',
        cobuyerprovince: '',
        cobuyerpostalcode: '99501',
        cobuyerphone: '',
        dobString: '',
        cobuyerDobString: '',
        isWaitList: false
      };
    }

    // Controller code for directive `checkbox`
    CheckboxController.$inject = ['$scope']; // <- This is needed only if you plan to minify your JS code.
    function CheckboxController($scope) {
      $scope.isChecked = false; // <-- Put all scope variables into the controller.

      $scope.isSameAddress = function () { // <-- put all of your scope functions in the controller.
        console.log('$scope.isChecked', $scope.isChecked);

        if ($scope.isChecked == true) {
          console.log('$scope.user', $scope.user);
        }
        else {

        }
      }
    }

    // Directive code for attribute `checkbox`
    function checkboxDirective() {
      return {
        controller: 'CheckboxController', // <-- I link to my controller and avoid adding the controller code here.
        restrict: 'A',
        replace: false,
        scope: {
          user: '='
        },
        template: '<label for="addressSame" class="form-label col-md-2" style="width: auto;">Same as Buyers Address</label><input type="checkbox" name="addressSame" ng-click="isSameAddress()" ng-model="isChecked" />'
      }
    }

    // Angular registration code
    var myApp = angular.module('myApp', []);

    myApp.controller('AppController', AppController);
    myApp.controller('CheckboxController', CheckboxController);
    myApp.directive('checkbox', checkboxDirective);
    </script>
  </head>
  <body ng-controller="AppController">
    <div checkbox user="user"></div>
  </body>
</html>

In your example you didn't show us how you were getting user into your directive. Based on your code example it should have been passed in through the user attribute. Like this:

<div checkbox user="user"></div>

Since you are using an Attribute based directive you could have also used the checked attribute instead of the user attribute like this:

// Directive code for attribute `checkbox`
function checkboxDirective() {
  return {
    controller: 'CheckboxController', // <-- I link to my controller and avoid adding the controller code here.
    restrict: 'A',
    replace: false,
    scope: {
      user: '=checkbox'
    },
    template: '<label for="addressSame" class="form-label col-md-2" style="width: auto;">Same as Buyers Address</label><input type="checkbox" name="addressSame" ng-click="isSameAddress()" ng-model="isChecked" />'
  }
}

This would allow you to get the outer controller's user value into your directive through the checkbox attribute. This value is then accessed in your Directive JS code through the $scope.user property. Like this:

<div checkbox="user"></div>

Upvotes: 0

sahed moral
sahed moral

Reputation: 385

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

app.directive('checkbox', function () {
    return {
        restrict: 'A',
        replace: false,
        transclude: true,
        $scope: {
            user: '='
        },
        template: '<label for="addressSame" class="form-label col-md-2" style="width: auto;">Same as Buyers Address</label><input type="checkbox" name="addressSame" ng-click="isSameAddress()" ng-model="isChecked" /> <span ng-if="isChecked"> {{user|json}}</span>',
        link: function ($scope, element, attrs) {
            // Initialize to unchecked
            $scope.isChecked = false;
            console.log($scope.user)

        },
        controller: function ($scope) {

            $scope.isSameAddress = function () {

                console.log($scope.isChecked);

                if ($scope.isChecked === true) {
                    console.log($scope.user);
                }
                else {

                }
            }

        }
    }
});

app.controller('MainCtrl', function ($scope, $http) {
  $scope.user = {
    addressline1: null,
    cobuyer: null,
    validate: null,
    cobuyerfirstname: null,
    cobuyerlastname: null,
    cobuyeremail: null,
    cobuyerdob: null,
    cobuyeraddressline1: null,
    cobuyeraddressline2: null,
    cobuyercity: null,
    cobuyerprovince: null,
    cobuyerpostalcode: null,
    cobuyerphone: null,
    dobString: null,
    cobuyerDobString: null,
    isWaitList: false
};
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p checkbox="user">Hello</p>
  </body>

</html>

Upvotes: 0

Harsha Jayamanna
Harsha Jayamanna

Reputation: 2258

Try this

myApp.controller('WizardController', ['$scope', function ($scope, $http) {

$scope.user = {
    addressline1: null,
    cobuyer: null,
    validate: null,
    cobuyerfirstname: null,
    cobuyerlastname: null,
    cobuyeremail: null,
    cobuyerdob: null,
    cobuyeraddressline1: null,
    cobuyeraddressline2: null,
    cobuyercity: null,
    cobuyerprovince: null,
    cobuyerpostalcode: null,
    cobuyerphone: null,
    dobString: null,
    cobuyerDobString: null,
    isWaitList: false
};

}]);

Upvotes: 1

chiconese
chiconese

Reputation: 325

That's because $scope is only visible inside the controller itself; You can use $rootScope (just remenber that it's a global variable); Try to change from $scope.user to $rootScope.user.

Upvotes: 1

Related Questions