4

I have a login form but for some reason the data is not being binded between the HTML and the controller. This is my code:

home.html

<div ng-if="loggedIn">
    <h2>Welcome!</h2>
    <p>Some other welcome content...</p>
</div>

<form ng-if="!loggedIn">
    <input type="text" placeholder="Username" ng-model="username">
    <input type="password" placeholder="Password" ng-model="password">
    <button type="submit" data-ng-click="login()">Login</button>
</form>

Note: labels, classes and ids ommited for brevity.

controller.js

myapp.controller('HomeController', ['$scope', '$log', function(scope, log) {
    scope.loggedIn = false;
    // ADDED CODE HERE

    scope.login = function() {
        // Dummy validation
        if(scope.username == 'admin' && scope.password == 'admin') {
            scope.loggedIn = true;
        };
        log.log('username: ' + scope.nombreUsuario);
        log.log('password: ' + scope.password);
        log.log('is logged in: ' + scope.loggedIn);
    };
}]);

config.js

myapp.config(['$locationProvider', '$routeProvider', function(locationProvider, routeProvider) {
    // Remove default '!' from URL
    locationProvider.hashPrefix('');

    routeProvider
    .when('/', {
        templateUrl: 'templates/home.html',
        controller: 'HomeController'
    })
    // ... some other .when ...
    .otherwise({
        redirectTo: '/'
    });
}]);

When I execute this, the console output of the login function is

username: undefined
password: undefined
is logged in: false

However, if I add the following to the controller in the //ADDED CODE HERE section

scope.username = 'admin';
scope.password = 'admin';

the console gives me the following:

username: admin
password: admin
is logged in: true

i.e. it's working correctly. So the question is: why my view (ng-model="...") doesn't bind to the controller's scope? what am I missing here?

Thanks in advance for your answer.

lin
  • 17,956
  • 4
  • 59
  • 83
Alvaro Pedraza
  • 1,176
  • 6
  • 20
  • 44

2 Answers2

3

Just access the $parent scope $parent.username, $parent.password and you will be fine like in this demo fiddle.

Some directives like ng-if does create a child scope so you need to access the parent scope. (Thx @FrankModica). E.g. if you are using ng-show or ng-hideyou dont need to access a $parent scope (Demo fiddle which uses ng-show).

<form ng-if="!loggedIn">
    <input type="text" placeholder="Username" ng-model="$parent.username">
    <input type="password" placeholder="Password" ng-model="$parent.password">
    <button type="submit" data-ng-click="login()">Login</button>
</form>
lin
  • 17,956
  • 4
  • 59
  • 83
  • This will work, but is not recommended because it is brittle. If the page is updated and new scopes are added you can run into issues. – Frank Modica May 19 '17 at 16:36
  • Great!, worked perfectly. But why do I need to access the parent scope? (what's the parent scope anyway? It's the first time I need to use it) Thank you so much – Alvaro Pedraza May 19 '17 at 16:38
  • I'll take into account the posible issue you mention. I've seen now the explanation in the edit. Thanks again – Alvaro Pedraza May 19 '17 at 16:41
2

Try binding to an object:

$scope.myForm = {
    userName: '',
    password: '',
    // ...
};

<input type="text" placeholder="Username" ng-model="myForm.username">

Because of prototypal inheritance, ng-model is initially reading from the parent scope (your controller). But as soon as the user types, ng-model ends up writing to the child scope created by ng-if, which inherits from the parent scope. If you bind to an object (not a primitive), you can be sure you will be reading and writing from the same place.

Frank Modica
  • 10,238
  • 3
  • 23
  • 39