AngularJS - Setting value of radio button built using ng-repeat - javascript

Here's the HTML :
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="$parent.selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>
The need is to store the selected answer into LocalStorage and whenever the question shows up again, mark the radio button corresponding to that answer.
I'm able to store it in LocalStorage, retrieve it, but when I update the model, it doesn't select the radio button on the UI.
In my controller, I'm simply calling -
$scope.selectedAnswer = value.answerID;
where value points to the answer stored in the LocalStorage.
Please help.
EDIT:
A little more detail :
I'm managing the entire quiz page using a single route and controller. When one question is done, I fetch the next question and its corresponding answers. While populating the answers array in the controller ($scope.answers), I'm checking to match their IDs with the answer stored in the LocalStorage. Between each question, the answers array is emptied and populated again.

So you should be using ng-value to do this since it is an angular expression and not a text value. Using just value as you did worked in Plnkr but I know that sometimes it does not.
Also in general I have found that using the ng-controller="<ControllerName> as <ThisRef>" works a lot better in most cases. There are times when you still need the $scope variable (Like for form validation) but normally I have fewer scoping issues when using the controllers "this" variable over the $scope.
I made an example using the method I described:
http://plnkr.co/edit/XV3we6SDmmLmUGSwdVVT?p=preview
If your question has to do with HTML 5 Local Storage let me know, I wasn't sure
EDIT:
Alternatively you can try using $scope.$apply(); to accomplish this. If the scope is not in sync with the view then apply should fix that.
Here is another plnkr showing how I would generally do something like that. Note that the $apply is 100% not needed in that example but likely is needed in yours.
Here is my example:
$scope.updateModel = function(value) {
$timeout(function() {
$scope.$apply(function() {
$scope.selectedAnswer = value;
});
});
};
Notice I am using $timeout to make sure it doesn't happen while there is a digest in progress.
http://plnkr.co/edit/O7CBfTiWPrsbthFKlAOZ?p=preview
I hope this edit helps =)

Since you only have one controller, you don't need to specify $parent.
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>

Related

Emit value on a search button depending on checkbox state, AngualrJS

My app has a Search Button, that hits an internal API with a GET Request. Then we fetch Data that are protected in some cases. I have a checkbox which is set to true by default to simulate the Back-End behavior.
If that box is unchecked then we add a parameter to our query GET Request protection: false. This tells the Back-End to give us access to unprotected Data.
My question is this. How to keep watch of the checkbox state in Angular Controller, and this change gets emitted to the search button?
I understand that I need to use $scope.watch for that, but AngularJS isn't my strong suit, so please help.
The protection parameter is passed as an Action from Redux, down to an RxJS Epic, to the Reducer and then injected to endpoint.js and service.js files that handle thoe requests.
That is done and working. My big problem is how to handle the angular part. Below you will find the code for the HTML and Controller files.
INDEX.HTML
<button class="btn btn-default btn-sm" ng-click="getData(something, protection)"
ng-disabled="Ctrl.loading">
Run Search
</button>
<span>Protection
<input type="checkbox" ng-model="Ctrl.Protection">
</span>
Controller.js
$scope.getData = (something, protection) => {
ctrl.getSomecData(protection, protection);
ctrl.saveRecentSearch(something);
};
So, one final go. I want to be able to check and uncheck the box, and that change should be emitted to the search button, and then inject that parameter in the GET Request.
Please help me do that in Angularjs. I haven't worked with it before this job, and it is really frustrating. Thanks for your time.
You just need to use ngChange directive of angular js on checkbox element.
method binded with ng-change attribute will called whenever checkbox is checked or unchecked.
further $watch is not recommended since it hits performance of the page.
Html:
<input type="checkbox" ng-model="Ctrl.Protection" ng-change="change()">
In your controller :
$scope.change = function() {
if(this.Protection){
//whatever you wish to do here
$scope.getData($scope.something, this.protection);
}
};

Angular 1.5 Radio Button Model

I have a radio button on a page whose ng-value is being set to an "answer" object. The model for that variable is being set to a particular element of an affiliations array. The structure of data looks something like this:
var user = { affiliations: [ {question: ..., answers: ... }, ... ] }
In this way, each "affiliation" has a question and one or more answers. I'm trying to create a UI for this in AngularJS like so:
<form name='affiliationsForm'>
<div ng-repeat="(i, affiliation) in DS_OrgAffiliations">
<h6>{{affiliation.question.name}}</h6>
<div ng-if="affiliation.question.questionType=='MultipleAnswer'">
...
</div>
<div ng-if="affiliation.question.questionType=='SingleAnswer'">
<label ng-repeat="answer in affiliation.answers">
<div>
<input type="radio" ng-model="$parent.DS_User.affiliations[i].answers" name="{{affiliation.question.name}}" ng-value="answer"> {{answer.answer}} </input>
</div>
</label>
</div>
</div>
<button data-ng-click="submitAffiliations()">save</button>
On my model, this actually works quite well. I have a little glue code that handles the differences between multiple choice questions and single questions, and if I make a change to this form, the model reflects the change without issue.
The problem is that when the form is initialized on the page, the correct radio button is never checked. The model will show an answer object inside the 'answers' property, but the form doesn't reflect that value. What's more, selecting a radio button assigns the correct answer to our model, and if I select what should be the active radio button, the model appears to not change.
I think what is happening is that the model is being initialized with a deep copy of the correct 'answer' object, rather than a shallow copy, and the fact that this reference is different is causing an issue, despite the objects having the same content.
Is this correct? To test this theory, I did this:
<input type="radio" ng-model="$parent.DS_User.affiliations[i].answers" name="{{affiliation.question.name}}" ng-value="answer" ng-checked="$parent.DS_User.affiliations[i].answer.answer==answer.answer"> {{answer.answer}} </input>
Notice that the ng-checked attribute is checking a member of the answers to ensure similarity. Unfortunately, this also fails to default the correct radio button on page load.
Any insight on this? I've spent about an hour and a half playing with this, and my references explanation seemed likely, but I haven't been able find a thread to pull on since the ng-checked attribute failed me.
So I spent a little more time with this and put together a simple fiddle. As it turns out, Angular only considers references when storing objects in ng-value, so my suggestion was actually right on. When storing primitives, it's possible to initialize the model with a different value, but when storing objects, you need to store a reference to the actual model entry!

AngularJS Watch Performance Issue

I need to code the following requirement. If it occurs that any form field changes, I need to hide a div with some content. My solution is using $watch with objectEquality == true for watching a complex object that bind to the form fields. But this complex object has around 100 fields to watch.
I think the solution described above addresses the requirement but I've read it could cause poor performance. So, is it the best solution? Do I have others alternatives ?
You may find that performance won't be an issue, 100 bindings isn't too bad. If you do need another solution though, you could put an ng-change listener on all the forms that want to watch, like so:
<input type="text" ng-change = "hideTheThing = true">
<div ng-hide = "hideTheThing"></div>
A lot of things that you think you need $scope.$watch for can and should probably be solved with an ng-change.
<input type="text" ng-change="formFieldChanged()">
Inside the function you can do whatver you like with other scope variables.

Weird Angular ng-model issue

I have a weird question regarding my Angular app.
I wanted to get the input texts when user enter texts in the input box.
my html
<input type="text" class="form-control" ng-model="texts" ng-change="type()"/>
my js
$scope.type = function() {
console.log($scope.texts)
console.log($scope) //doesn't see texts property either
}
However, every time I type something, the first console log shows 'undefined'. It's like controller can't find the texts property. Can anyone help me this weird issue? Thanks a lot!
This can be accomplished by using angular's two-way data binding system:
Data-binding in Angular apps is the automatic synchronization of data
between the model and view components. The way that Angular implements
data-binding lets you treat the model as the single-source-of-truth in
your application. The view is a projection of the model at all times.
When the model changes, the view reflects the change, and vice versa.
This allows us to synchronize the properties of our controller's scope by binding to them in our html.
So, we might define our controller like this:
myApp.controller('SomeController', function ($scope) {
$scope.text = "";
});
Now, we can use this in our html like this:
<div ng-controller="SomeController">
<input type="text" class="form-control" ng-model="text" />
</div>
The important thing to notice here is the ng-model directive. Here we bind the text property we defined in our controller to the <input> field. Now, with two-way binding, if we type into the input field it will automatically update the text property. This way we can access the value of the input field within our controller without having to do any extra work.
Here is a plnkr with a working example: http://plnkr.co/edit/xEkoDbPxwrWgGfuEiS3a?p=preview
Hope this helps :-)
$scope has to be passed in to the controller. Check your controller arguments.

Splitting HTML components for one controller causes controller to stop working

If I create two div's, both controlled by the same controller, Angular's controller seems to stop updating my view.
I've put together a bare-bones example to demonstrate this. It is easiest to view the sample here on JSFiddle, but I'm also posting the code below.
HTML
<div ng-app='App'>
<div ng-controller="MyCtrl">
<form>
<input type="text" ng-model="search.query"></input>
<button ng-click="search.submit()">Submit</button>
</form>
</div>
<div ng-controller="SomeOtherCtrl">
{{sampleText}}
</div>
<div ng-controller="MyCtrl">
<button ng-click="search.reset()">Reset Form</button>
</div>
</div>
JS
var app = angular.module('App',[]);
function MyCtrl($scope)
{
$scope.search = {
query : 'foobar',
submit : function() {
this.query = 'Submitted';
console.log(this.query);
},
reset : function() {
console.log('resetting');
this.query = '';
}
};
}
function SomeOtherCtrl($scope) {
$scope.sampleText = 'other controller text';
}
The submit button works fine, but when I click on the Reset button, I see resetting get logged in the console, but my view (the form input) does not update.
Why is this so? In a current project I'm working on, I'm forced to do this because certain HTML that falls in between belongs to another controller. How can I work around this?
A controller definition in angular is actually a Class / Constructor and not an Object. Each place in the HTML that controller is referenced, during the compilation phase, angular creates a new controller object using the defined controller class. So, you could refer to more than one scope with the same controller class.
You might have already heard of services in Angular and this is exactly a place where you need to use them to solve it. search that you are using is a common object that is being shared by two controller instances ( Note the class is the same MyCtrl but by putting in two ng-controller="MyCtrl" you asked Angular to create two instances of MyCtrl ). So these two objects have access to two different scopes and when they are created the two different scopes are set with two different search objects. When you click on reset, the other search objects reset gets called, which means nothing happens in the first MyCtrl's scope. This is the reason why your code doesnt work as expected.
Now note that in angular services are singletons. So if you reference them in multiple places you get access to the same object. So, resetting in a different controller (instance) will still reset the same object.
Here is a plunk that works with your code.
http://plnkr.co/edit/zynAdS9hg8DUZDnlnYFm?p=preview
I have no idea how angularjs works but if you put 2nd input <input type="text" ng-model="search.query"></input> near to reset button, this input will be updated.
Should inputs be inside the same div controller?
Will it help you to solve your problem?
Looking at your sample it seems that you are missing form tag for reset button. From angular perspective if you can use one outer div for controller="MyCtrl" where you have a form with two buttons-submit() and reset() and inside the outer div you put your nested controller "SomeOtherCtrl" then it should work perfectly.

Categories