1

Since I began learning AngularJS I've seen different approaches for calling controller functions from a view.

Suppose we have a Todo list application in AngularJS where you can add and remove Todo list items:

  function TodoListCtrl() {
    var vm = this;
    vm.addItem = addItem;
    vm.removeItem = removeItem;

    activate();

    function activate() {
      vm.list = [];    
    }

    function addItem() {
      vm.list.push(vm.newItem);
      // reset the form
      vm.newItem = null;
    }

    function removeItem(item) {
      vm.list.splice(vm.list.indexOf(item, 1));
    }
  }

And our HTML:

<h3>Todo List</h3>

<ul>
  <li ng-repeat="item in vm.list">
    {{ item }} <a ng-click="vm.removeItem(item)">Remove</a>
  </li>
</ul>


<h4>Add Item</h4>

<input type="text" ng-model="vm.newItem" /> <button ng-click="vm.addItem()">Add</button>

In this example the addItem function depends on vm.newItem being set in order to add the new list item. However, it could also be re-written as:

function addItem(item) {
  vm.list.push(item);
  // reset the form
  vm.newItem = null;
}

With our HTML updated as so:

<button ng-click="vm.addItem(vm.newItem)">Add</button>

I can see that this makes the function easier to test since we're not dependent on the state of the controller but we don't avoid it completely since we're resetting vm.newItem after an item is added.

Are there any best practices for when we should pass parameters from our views and when we can just rely on the internal state of the controller?

2
  • I think that's a per-case situation, shouldn't matter much in yours. In your case I'd do a ng-disabled="!vm.newItem" on the button as well, maybe wrap with an if (item) { } in the controller function. Commented Feb 11, 2015 at 15:13
  • I dont think that really matters... It is just the same as ask if you should use field value in java/c# class or add additional parameter to method. You can use or not vm.newItem in controller - it is still there. Commented Feb 11, 2015 at 15:18

2 Answers 2

2

Passing vm.newItem means you have it in 2 places in the View. While it may be clear, it's also repeating yourself and leaves you open to possibly having one get out of sync. And at what additional value? I think it is clear already without that like this.

<input type="text" ng-model="vm.newItem" />
<button ng-click="vm.addItem()">Add</button>

Otherwise you have this duplication.

<input type="text" ng-model="vm.newItem" />
<button ng-click="vm.addItem(vm.newItem)">Add</button>

You say it is easier to test, but why? You are testing a function on the controller, so it's perfectly fine to expect that function is use a property on the same controller. You mock dependencies to the controller, but not members of it.

Sign up to request clarification or add additional context in comments.

Comments

1

In both cases you showed, the function addItem creates side-effects on the internal state of the controller (with vm.newItem = null;), so it cannot be tested in isolation.

In the second case, however, this doesn't even make sense to pass a different variable, since it would make the statement vm.newItem = null; potentially erroneous.

To make the function completely state-less:

vm.addItem(item){
   vm.list.push(item);
}

you'd need to reset the form from the View:

<input type="text" ng-model="vm.newItem">
<button ng-click="vm.addItem(); vm.newItem = null">Add</button>

This could be acceptable if resetting the form is a View-only concern. If not, then this approach would not work altogether because it could leave your controller in an erroneous state (where vm.newItem is still pointing to the newly added item of the list)

At the end of the day, it depends on your particular use case. If you always have only a single item that you can "add", then passing an explicit parameter is redundant at best.

If, however, addItem can be called for any newly created item in the View, then passing the reference explicitly is probably the only way to go.

Testing-wise, you should always test that a controller is in a consistent state after an operation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.