Angular radio button scope - angularjs

I'm working on a radio button list where a user can select from a pre-populated list of problems, or select an "other" radio button and then type in their specific problem.
I can get the pre-populated list of radio buttons to work and set the problem (outputting the scope variable confirms this), but introducing the "other" functionality is stumping me. When I select other, it doesn't seem to bind to the scope variable. I noticed in the dom it's missing an class="ng-scope" that the other radio buttons seem to get from the ng-repeat, but I'm not sure if that's the problem.
<form>
// This part loops through the list of problems and makess radio buttons
<div ng-repeat="problem in selectedType['nature_of_problem']">
<input type="radio" ng-model="$parent.natureOfProblem" ng-value="problem"/>
</div>
// Ideally this part is where the "other" radio is, it's still in the form
<input type="radio" ng-model="natureOfProblem" ng-value="other" ng-checked="">
</form>

Working JSFiddle:
http://jsfiddle.net/HB7LU/3794/
I saw a few issues, among them:
Using ng-value instead of plain old value for "other"
Using a primitive instead of dot notation (if you want your view to reliably write a variable, it needs to be something.yourVariable instead of just plain old yourVariable)
Hope this helps!
function MyCtrl($scope) {
$scope.uiState = {};
$scope.uiState.natureOfProblem = 1;
$scope.selectedType = {};
$scope.selectedType.nature_of_problem = [1,2,3];
}
<div ng-controller="MyCtrl">
<p>Nature of problem is: {{uiState.natureOfProblem}}</p>
<form>
<div ng-repeat="problem in selectedType['nature_of_problem']">
<input type="radio" ng-model="uiState.natureOfProblem" ng-value="problem"/><span ng-bind="problem"></span>
</div>
<input type="radio" ng-model="uiState.natureOfProblem" value="Other" /><span>Other</span>
</form>
</div>
EDIT to answer OP's questions:
I tend to use ng-bind out of habit -- in slower browsers like Firefox, it keeps "{{blah}}" from showing up on the screen as everything loads. Newer versions of Angular also have ng-cloak for this purpose, which I should probably get in the habit of using instead. :) (I also vaguely remember reading that "{{blah}}" can cause issues in IE, but I very possibly made that up.)
The use of dot notation relates to the fact that Angular can't maintain data bindings on brand-new objects. To try to explain it without using terms like "scope" and "inheritance": If you influence an existing object by changing yourObject.anAttribute, the overarching object consistently exists throughout that process and does not drop its binding. But if you have blahVariable that is equal to 8, and you set blahVariable equal to 7, you've basically tossed the old piece of data and created a new piece of data entirely. This new piece does not maintain the binding, so the controller never gets the memo from the view that the value has changed.
Sometimes I find this useful, actually -- you can briefly manipulate a variable in the view for some quick-and-dirty purpose without the controller finding out about it. :)

Related

What is the advantage of "Controller as" in Angular?

What is the advantage of using "Controller as" syntax in Angular? Is it just to create an alias for a controller or does it have some other technical reasons behind the scenes?
I am new to Angular and want to know more about this syntax.
controllerAs-syntax has multiple advantages:
Clartiy
Consider the following example:
<div ng-controller="containerController">
<h2>Improve your life!</h2>
<p ng-controller="paragraphController">
We talk about {{topic}} a lot, but do we really understand it?
Read this article to enhance your knowledge about {{topic}}
</p>
</div>
Just by reading this piece of code, you can not tell where topic comes from. Does it belong to the containerController, to the paragraphController or is it just a random floating scope variable from sone input above?
By using controllerAs it is very clear:
<div ng-controller="containerController as container">
<h2>Improve your life!</h2>
<p ng-controller="paragraphController as paragraph">
We talk about {{paragraph.topic}} a lot, but do we really understand it?
Read this article to enhance your knowledge about {{paragraph.topic}}
</p>
</div>
You can immediately see that topic is a property of paragraphController. This makes code a lot more readable overall, as it forces the developer to make clear who the functions and variables in the scope belong to.
Binding to properties
When you are using the old controller syntax, strange things can happen when you have multiple bindings in different scopes to the "same" variable. Consider this example:
<form ng-controller="myFormController">
<input type="text" ng-model="somefield">
<div ng-controller="someOtherController">
<input type="text" ng-model="somefield">
</div>
</form>
It looks like both inputs are bound to the same variable. They are not. Everything looks like it works fine when you edit the first input first, but as soon as you edit the second one, they won't sync up anymore. This has to do with the way scope-inheritance and binding work(and there is an excellent answer on this on SO). This does not happen when you bind to object properties (aka when there is a . in your ng-model-attribute). With controllerAs you bind to properties of the controller objects anyways, so it naturally solves that problem:
<form ng-controller="myFormController as myForm">
<input type="text" ng-model="myForm.somefield">
<div ng-controller="someOtherController as other">
<input type="text" ng-model="myForm.somefield">
</div>
</form>
It gets rid of scope(mostly)
The use of scope to create bindings to controllers in old angular code is hard to read, hard to understand and completely unnecessary if you use controllerAs. You no longer have to inject scope into every single controller, in fact you will probably not inject it in any in most applications (you still need to do so if you want to use the angular event system). This results in cleaner controller-code with less strange boilerplate.
It prepares for Angular 2
In angular 2, scope will be gone and we will write everything as components. Using controllerAs lets you work without scope and forces you to think more component-oriented, thus preparing you (and the applications you eventually will want to migrate) for the 2.0 update.

What scenarios force me to setup workarounds for ng-model in angular?

Typically in almost all situations, I am able to setup ng-model on an element in the standard fashion as follows.
<textarea ng-model="MessageText"></textarea>
But sometimes ng-model does not update within the scope. I have to force it to work by doing this
<textarea ng-model="MessageText" ng-change="UpdateMessageTextScope(MessageText)" ></textarea>
$scope.UpdateMessageTextScope = function (MessageText) {
$scope.MessageText = MessageText;
}
In the above setup, if I leave out the ng-change, ng-model appears to be very buggy. Adding {{MessageText}} to my html seems to indicate MessageText is properly updating, but when it comes time to submit data to an API, MessageText contain different data than what {{MessageText}} on the UI is showing me. By different data, I mean it only updates the ng-model value 1 time... if I type new data into the textarea, the UI output indicates it updated to the new value, but the scope is stuck using the original value.
It's not very frequent (1% or less), but I am confused as to what causes this in the first place.

What magic does ngModel use that modifying the scope directly does not?

Learning angularjs at the moment, and I am confused as to how I can accomplish a task because I don't fully understand what ngModel is doing.
If I have a directive with two scope variables:
// An array of all my objects
$scope.allMyObjects
// The currently selected object from the array
$scope.selectedObject
and in the html
<span>{{ selectedObject.name }}</span>
<select id="select"
ng-model="selectedObject"
ng-options="object in allMyObjects">
</select>
This all works perfectly, when I select an object from the select, it updates the selectedObject on the scope and so the name of the currently selected object is displayed.
However, I don't want a select box, instead I want a list of all my objects with an editable name field, with a select button that I can use to select the specified object, so I came up with the following:
<div ng-repeat="object in allMyObjects">
<input class="object-name"
ng-model="object.name">
<a ng-click="loadObject(object)">Load</a>
</div>
and the loadObject() function on the scope:
function loadObject(object) {
$scope.selectedObject = object;
}
However, this doesn't work. I had assumed this was basically what ngModel was doing behind the scenes but am obviously confused. Is anyone able to shed some light or offer a better solution to what I wish to achieve?
Please see here :http://jsbin.com/jocane/1/edit?html,js,output
use ng-model="object.name" instead "sc.name"
<div ng-repeat="object in allMyObjects">
<input class="object-name"
ng-model="object.name">
<a ng-click="loadObject(object)">Load</a>
</div>
After an hour of debugging it came down to an issue with the scope being isolated by the ng-repeat, the problem didn't show up in any of the simplified jsfiddle examples because they used pure JS and it was the way I was accessing the scope via typescript that caused the issue.
Thanks for the answers that helped me narrow it down to my difficulty understanding typescript and not my difficulty understanding directives.

ngRepeat breaks the Foundation Switch CSS. How can I fix it?

I'm trying to use the Switch component from Zurb's Foundation.
It works great until you put it inside an ng-repeat. Then, all the switches except the last one are broken--they don't display the labels until you click them.
Here's a JSBin documenting the issue. Anyone know what's up?
You need ng-model on your radios. It works fine in your JSBin if you include an ng-model on each radio button.
According to the docs, value is also required.
Edit:
Okay look at this version, I finally got it to work with both ng-model and ng-value, which I've learned is preferable to value. What do you think, does that work for you?
Apparently ng-repeat created a child scope for each iteration. Using $parent seems to be a way around that.
Found I had to use "ng-checked" in the following fashion for it to work as expected within an "ng-repeat". As long as the ng-clicked var is unique per switch, should work as expected. Make the toggle function something that toggles the visible value between true and false.
<div class="switch" ng-click="toggle()">
<input type="radio" ng-checked="!visible">
<label>Off</label>
<input type="radio" ng-checked="visible">
<label>On</label>
<span></span>
</div>
Hope that helps someone.

Dynamic data-binding in AngularJS

I'm building an AngularJS app and I have ran into an issue. I have been playing with the framework for a while and I have yet to see documentation for something like this or any examples. I'm not sure which path to go down, Directive, Module, or something that I haven't heard of yet...
Problem:
Basically my app allows the user to add objects, we will say spans for this example, that have certain attribute's that are editable: height and an associated label. Rather than every span have its own dedicated input fields for height and label manipulation I would like to use one set of input fields that are able to control all iterations of our span object.
So my approx. working code is something like this:
<span ng-repeat="widget in chart.object">
<label>{{widget.label}}</label>
<span id="obj-js" class="obj" style="height:{{widget.amt}}px"></span>
</span>
<button ng-click="addObject()" class="add">ADD</button>
<input type="text" class="builder-input" ng-model="chart.object[0]['label']"/>
<input type="range" class="slider" ng-model="chart.object[0]['amt']"/>
The above code will let users add new objects, but the UI is obviously hardcoded to the first object in the array.
Desired Functionality:
When a user clicks on an object it updates the value of the input's ng-model to bind to the object clicked. So if "object_2" is clicked the input's ng-model updates to sync with the object_2's value. If the user clicks on "object_4" it updates the input's ng-model, you get the idea. Smart UI, essentially.
I've thought about writing a directive attribute called "sync" that could push the ng-model status to the bound UI. I've though about completely creating a new tag called <object> and construct these in the controller. And I've thought about using ng-click="someFn()" that updates the input fields. All of these are 'possibilities' that have their own pros and cons, but I thought before I either spin out on something or go down the wrong road I would ask the community.
Has anyone done this before (if so, examples)? If not, what would be the cleanest, AngularJS way to perform this? Cheers.
I don't think you need to use a custom directive specifically for this situation - although that may be helpful in your app once your controls are more involved.
Take as look at this possible solution, with a bit of formatting added:
http://jsfiddle.net/tLfYt/
I think the simplest way to solve this requires:
- Store 'selected' index in scope
- Bind ng-click to each repeated span, and use this to update the index.
From there, you can do exactly as you proposed: update the model on your inputs. This way of declarative thinking is something I love about Angular - your application can flow the way you would logically think about the problem.
In your controller:
$scope.selectedObjectIndex = null;
$scope.selectObject = function($index) {
$scope.selectedObjectIndex = $index;
}
In your ng-repeat:
<span ng-repeat="widget in chart.object" ng-click="selectObject($index)">
Your inputs:
<input type="text" class="builder-input" ng-model="chart.object[selectedObjectIndex]['label']"/>
<input type="range" class="slider" ng-model="chart.object[selectedObjectIndex]['amt']"/>

Resources