0

In the function $scope.addTodo(), my browser is only evaluating the if statement upon the first entry onto the $scope.todos array. After I create a single todo, I can now push blank todos, even though that is not the desired outcome. What is strange too, is that if I change the if statement from

if ($scope.formTodoText != null)

to

if ($scope.formTodoText != "")

then I can submit a blank todo, but only once. After that initial blank todo is submitted, there are no problems with the evaluation.

<!DOCTYPE html>
<html ng-app>
<head>
<script src="http://documentcloud.github.io/underscore/underscore-min.js"></script>
<script src="http://code.jquery.com/jquery.min.js"></script>
<link href="http://twitter.github.io/bootstrap/assets/css/bootstrap.css" rel="stylesheet" type="text/css" />
<link href="http://twitter.github.io/bootstrap/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css" />
<script src="http://twitter.github.io/bootstrap/assets/js/bootstrap.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
<style type="text/css">
  .done-true {
    text-decoration: line-through;
    color: grey;
  }
</style> 

</head>
<body>

  <div ng-controller="TodoCtrl" class="container">

    <h2>Total Todos: {{getTotalTodos()}}</h2>
    <ul class="unstyled">
      <li ng-repeat="todo in todos">
        <input type="checkbox" ng-model="todo.done"> 
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form class="form-horizontal">
      <input type="text" ng-model="formTodoText" ng-model-instant>
      <button class="btn" ng-click="addTodo()"><i class="icon-plus"></i>Add</button>
    </form>
    <button class="btn-large" ng-click="clearCompleted()"> <i class="icon-trash"></i> Clear</button>
  </div>
  <script type="text/javascript">
    function TodoCtrl($scope) {


      $scope.todos = [
        {text: 'Learn angular', done: false},
        {text: 'Build an app', done: false},
        {text: 'Design UI', done: false}
      ];

      $scope.getTotalTodos = function() {
        return $scope.totalTodos = $scope.todos.length;
      };

      $scope.clearCompleted = function () {
        $scope.todos = _.filter($scope.todos, function (todo) {
            return !todo.done;
        });
      };

      $scope.addTodo = function() {
        if ($scope.formTodoText != null) {
            $scope.todos.push({text:$scope.formTodoText, done: false});
            $scope.formTodoText = "";
        }
      };
    }

  </script>
</body>
</html>
messivanio
  • 2,263
  • 18
  • 24
user1876508
  • 12,864
  • 21
  • 68
  • 105
  • Where does `ng-model-instant` come from? – Stewie Jul 06 '13 at 12:38
  • possible duplicate of [Is there a standard function to check for null, undefined, or blank variables in JavaScript?](http://stackoverflow.com/questions/5515310/is-there-a-standard-function-to-check-for-null-undefined-or-blank-variables-in) – Stewie Jul 06 '13 at 12:41

1 Answers1

2

First off, you should use the !== and === operators (instead of the != and == operators) for comparison in JavaScript.

You might also find console.log handy for this. Try adding

    console.log($scope.formTodoText);

as the first line of the $scope.addTodo function body to see what it contains.

For the first case, where you used:

    if ($scope.formTodoText != null) {

Initially, we cannot submit empty todo (without doing some kind of editing on the input field) because $scope.formTodoText is undefined. This causes the if ($scope.formTodoText != null) check to fail, so it does not get added. However, if you type some characters into the input field and then erase them, you will find that it is still possible to add empty todos, since $scope.formTodoText will become the empty string and '' != null, so we enter the if body and add the empty todo.

Now, suppose we create a single, non empty todo. This calls the $scope.addTodo function. Since $scope.formTodoText is not null, this pushes the todo onto the list. Then, it resets $scope.formTodoText to the empty string. This is the reason why we were able to add empty todos from here on without editing the contents of the input field, because $scope.formTodoText is set to the empty string and '' != null

If instead, we use:

    if ($scope.formTodoText != '') {

and then click the add button (without doing anything to the input field prior to that), then $scope.formTodoText will be undefined, and undefined != '', so we go into the body of the if statement. This is followed by a push of {text: undefined, done: false} into $scope.todos. After that, $scope.formTodoText is set to the empty string, and adding todos which are the empty string fails from this point on.

To prevent empty todos, one way is to change the condition to:

    if ($scope.formTodoText) {

so $scope.formTodoText will check that $scope.formTodoText is a "truthy" value, which automatically excludes "falsy" values, in particular undefined and the empty string. For more information on truthy and falsy values, see http://docs.nodejitsu.com/articles/javascript-conventions/what-are-truthy-and-falsy-values

yanhan
  • 3,507
  • 3
  • 28
  • 38