Blitz Blitz - 5 months ago 9
Javascript Question

How does childscope work on an ng-if statement? Specifically in a <SELECT> element

NOTE: I'm a new member here so I couldn't directly comment and ask for clarification.

So, my question is: How can I work around ng-if creating a child scope for a select element?

I have the following code:

HTML

<select id="project-select"
ng-if="projects.length > 0"
ng-options="project.name for project in projects"
ng-model="currentProject"
ng-change="broadcastChange('project-changed', currentProject)">
</select>


And my controller is set up in the following format:

function Controller() {
//Do code stuffz
}
angular
.module('app')
.controller('Controller', Controller);


I learned from this post that the "ng-if" is creating a child scope.

So even when the model changes, this part stays the same because it is a primitive value: (name is just a string)

<div id="current-project" class="pull-left">
<strong>Project: </strong>{{currentProject.name}}
</div>


Furthermore in the aforementioned post there were a couple options.

a. Simply change to this:
ng-model="$parent.currentProject"
which feels a little hacky

b. Set the object value in the controller, which I'm not entirely sure how to do. I feel like it's an easy fix, but I'm somehow overcomplicating it.

Anyway, for now I simply changed
ng-if
to
ng-show
and that's solved the problem. However, I am trying to understand Angular more deeply and I feel like this issue could be explained a little bit better to me. Thanks in advance!

Answer

What you will find with Angular scope variables is: always use a dot.

That's the mantra from the excellent ng-book

In your case, what this means is this:

You have this code:

    <select id="project-select" 
        ng-if="projects.length > 0"
        ng-options="project.name for project in projects" 
        ng-model="currentProject"
        ng-change="broadcastChange('project-changed', currentProject)">
    </select>

Which means that you are binding to a $scope variable called $scope.currentProject.

Because of the mysterious and awesome way that javascript works, this does not get updated when you are inside of a child scope.

Thankfully, the solution is actually quite simple. Instead, create an object like so:

$scope.myData = {
    currentProject: ''
}

And in your markup, bind to that like so:

    <select id="project-select" 
        ng-if="projects.length > 0"
        ng-options="project.name for project in projects" 
        ng-model="myData.currentProject"
        ng-change="broadcastChange('project-changed', myData.currentProject)">
    </select>

And voila. It will update, even though it's in a child scope.

This is actually quite useful, because you now have a way to "meaningfully" group variables together. Here's some other pseudo-code to demonstrate what I mean:

$scope.projectData = {
    currentProjectID: 1,
    currentProjectTitle: 'My Cool Project',
    projects: [
             {id: 1, name: 'My Cool Project'},
             {id: 2, name: 'Another Project'}
         ],
    someOtherProperty: false
    // ...etc....
}

As a side-note, this section of this article might be helpful: http://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm#jscript4-CHP-11-SECT-2.1