In my application I have a list of different "questions" each question refers to one of two components controlled by a variable QuestionIndex that basically takes the current object in the list.
So I have Component A, which has a form inside of it with two Radio buttons:
<form [formGroup]="form">
<label>
<span class="inlineBlock">
<input type="radio" value="Ja" formControlName="response" class="radioInput">
<span class="labelSpan">Ja</span>
</span>
</label>
<label>
<span class="inlineBlock">
<input type="radio" value="Nej" formControlName="response" class="radioInput">
<span class="labelSpan">Nej</span>
</span>
</label>
</form>
I fill out the form and increase the QuestionIndex.
Then the form inside of that component is not reloaded but already filled out.
How can I ensure that the component is completely reloaded?
I have tried with ngOnChanges but this won't really work because there are so many values I need to reset and each reset fires the ngOnChanges again.
How it's used
Below is how the component is used when the index is increased the currentQuestion object is changed and therefore the component is loaded
<app-radio-question [text]="currentQuestion.text"
*ngIf="currentQuestion.type == 'RadioQuestion' && !showEndQuestion"
[title]="currentQuestion.title" [NoOptionText]="currentQuestion.noOption"
[YesOptionText]="currentQuestion.yesOption"
[cacheData]="cacheData"
(formChangedEvent)="OnQuestionResponse($event)"></app-radio-question>
However this component is changed but not updated this means that if I change a value inside of it I get the ExpressionChangedAfterItHasBeenCheckedError error.
This input is not bound to any variable at all.
You should consider doing that and just reset that specific variable
HTML
<input [(ngModel)]="inputVariableName" (change)="onChangeFunc()" value="true" formControlName="response" class="radioInput"/>
TS
inputVariableName = false; // initial
onChangeFunc() {
inputVariableName = false; //set it to initial again
}
But you have to import FormsModule for this to work.
I would suggest that you used some kind of framework for radio buttons. I use Angular Material and the radio buttons is grouped like this:
<mat-radio-group aria-label="Select an option">
<mat-radio-button value="1">Option 1</mat-radio-button>
<mat-radio-button value="2">Option 2</mat-radio-button>
</mat-radio-group>
A full example can be find here Radio Button Angular Material.
Related
Radio button's disabled property is not working inside the reactive form but it's working fine when I put radio button outside the reactive form.
I have a condition like if my current status is CLOSED I should allow the user to edit radio button inside the reactive form I already tried with Disabled property of radio button but no luck
component.ts
conducttestlm:any={
isReadOnly:false
}
this.condutTestLM=this.formBuilder.group({
"testStatus":['',Validators.required],
})
if(success.data.status=='CLOSED'){
this.conducttestlm.isReadOnly=true;
}
component.html
<form [formGroup]="condutTestLM">
<div class="row radio-top">
<div class="col-md-6 p-l-15">
<label class="radio-box">
<input formControlName="testStatus" name="testStatus" [(ngModel)]="conducttestlm.teststatus" [disabled]="conducttestlm.isReadOnly" value="Pass" type="radio">
<span class="checkmark"></span>
<span class="font-style">Pass</span>
</label>
</div>
<div class="col-md-6 p-l-15">
<label class="radio-box">
<input formControlName="testStatus" name="testStatus" [(ngModel)]="conducttestlm.teststatus" [disabled]="conducttestlm.isReadOnly" value="Fail" type="radio">
<span class="checkmark"></span>
<span class="font-style">Fail</span>
</label>
</div>
</div>
</form >
I need to disable the radio button
Don't use template driven approach while using reactive forms. Have your code like:
<form [formGroup]="condutTestLM">
<div class="row radio-top">
<div class="col-md-6 p-l-15">
<label class="radio-box">
<input formControlName="testStatus" name="testStatus" [attr.disabled]="conducttestlm.isReadOnly ? true : null" value="Pass" type="radio">
<span class="checkmark"></span>
<span class="font-style">Pass</span>
</label>
</div>
<div class="col-md-6 p-l-15">
<label class="radio-box">
<input formControlName="testStatus" name="testStatus" [attr.disabled]="conducttestlm.isReadOnly ? true: null" value="Fail" type="radio">
<span class="checkmark"></span>
<span class="font-style">Fail</span>
</label>
</div>
</div>
</form>
See an example here: https://stackblitz.com/edit/angular-dncxac?file=src%2Fapp%2Fapp.component.html
You will notice that I have used attr.disabled instead of disabled to disable individual radio buttons.
To know the difference between attr.disabled and disabled, you can have a look at this link. In a gist, attr.disabled is an HTML attribute while disabled is a DOM property. There are some HTML attributes for which DOM properties don't exist, as shown in the added link.
From angular docs
Attributes are defined by HTML. Properties are defined by the DOM (Document Object Model).
A few HTML attributes have 1:1 mapping to properties. id is one example.
Some HTML attributes don't have corresponding properties. colspan is one example.
Some DOM properties don't have corresponding attributes. textContent is one example.
Many HTML attributes appear to map to properties ... but not in the way you might think!
This is not true for input boxes and disabled DOM property. There indeed is a disabled DOM property but there is some issue while using it individually over radio buttons. See this Github issue. The solution I provided is more of a workaround to achieve individual disabling of radio buttons.
One possibility is that the status change is not being detected while rendering. First of all:
// instead of using this:
[disabled]="conducttestlm.isReadOnly"
// Use the readonly attribute:
[readonly]="conducttestlm.isReadOnly"
Next, use ChangeDetectorRef to detect any changes manually:
// import it:
import { ChangeDetectorRef } from '#angular/core';
// and inject it into your contructor:
constructor(private ref: ChangeDetectorRef) {
...
}
// call it
if(success.data.status=='CLOSED'){
this.conducttestlm.isReadOnly=true;
this.ref.detectChanges();
}
Should detect changes in status immedietly on run. If not, try console logging your conducttestlm.isReadOnly value to ensure it's changing.
If you want to use template driven attribute, you can use [attr.disabled] instead [disabled] as well.
what i am trying to do here is, i have an ng-repeat in a form and if i click anyone of those input buttons corresponding all buttons get disabled
<div ng-repeat="question in sinSurCtrl.singleSurvey" ng-show="!$first">
<hr>
<p>Q. {{question.questionText}}</p>
<form >
<div ng-repeat=" option in question.questionOptions track by $index">
<label>
<input name="options" type="radio" value="$index" ng-click="sinSurCtrl.questionId=question.questionId; sinSurCtrl.answer=$index+1; sinSurCtrl.createAnswer()" onclick="this.disabled = true">
<span> {{option}} {{$index+1}} {{question.questionId}} </span>
</label>
</div>
</form>
</div>
here is the view-
as you can see if i select anyone of those option it is getting disabled but what i am trying to do is if i attempt anyone option then options corresponding to the same question get disabled.
for example-
in Q3. which is a better orator ?
if i choose option (a) then it get selected and after that automatically bot options (a) and (b) get disabled.
Note- please try to keep solution completely in angularjs or if you want use affordable amount of javascript other then that please avoid using external libraries like jQuery(i know nobody in his senses will handle trouble of using jQuery and angular together but for the sake of example i have included its name)
Proposed solution with some suggested refactoring...
First change the ng-click directive to point to a new onOptionButtonClicked function on sinSurCtrl which takes in the two parameters question and index (which it needs to carry out it's work):
<div ng-repeat="question in sinSurCtrl.singleSurvey" ng-show="!$first">
<hr>
<p>Q. {{question.questionText}}</p>
<form>
<div ng-repeat="option in question.questionOptions track by $index">
<label>
<input
name="options"
type="radio"
value="$index"
ng-click="onOptionButtonClicked(question, $index)"
ng-disabled="question.disabled">
<span> {{option}} {{$index+1}} {{question.questionId}} </span>
</label>
</div>
</form>
</div>
Also take note of the newly added ng-disabled="question.disabled" directive. This is part of the mechanism that will enable/disable the question's controls.
Now move the variable assignments to the new onOptionButtonClicked function. The controller is generally a better place (than the view) for variable assignments, especially if there are several of them on the same directive.
sinSurCtrl.onOptionButtonClicked = onOptionButtonClicked;
function onOptionButtonClicked(question, index){
sinSurCtrl.questionId=question.questionId;
sinSurCtrl.answer=index;
sinSurCtrl.createAnswer();
question.disabled = true; // <--- This is what disables the option buttons
}
This is where an answered question object gets it's disabled property set to true. This in combination with the ng-disabled directive mentioned previously is what disables the option buttons.
On your controller, create a function that checks if a given questionId has been answered and return truthy if it has. Call that function in ng-disabled in the input tag:
<input type="radio" ng-disabled="sinSurCtrl.questionHasAnswer(question.questionId)" ... />
I am using AngularJS v1.4.3 and Bootstrap v3.3.6.
I have created a list of radio buttons like below
<div>
<label>Pizza</label>
<ul ng-repeat="option in pizzaOptions">
<input type="radio" name="pizza" ng-model="pickedPizza"
ng-value="{{option}}">
{{option.displayText}}
</ul>
</div>
ngModel- pickedPizza is an object.
When selecting an option the user's selection is saved correctly. The radio button shows the user has selected an option. Once the user navigates to the next page and back the option is no longer selected although ngModel is still populated correctly.
When the ngModel is not an object and I do not need to use ngValue (like below), navigating away from the page and back repopulates the selection fine.
<div>
<label>Drink</label>
<ul ng-repeat="option in drinkOptions">
<input type="radio" name="drink"
ng-model="drinkSelected" value="{{option}}">
{{option}}
</ul>
</div>
As georgeawg said, the model needs to be a dot object
so instead of $scope.pickedPizza = {displayText: 'cheese'} it needs to be $scope.pickedPizza = { value : {displayText: 'cheese'} }; and ng-model='pickedPizza.value' or something similar.
Demo
I have a very simple form where I have Yes and No radio buttons. Each radio button is bound to the same item in the scope (I am using AngularJS). The Yes button's value gets set to true on being selected and the No button's value gets set to false when being selected.
When I click the Yes button once, both the model and the html element changes correctly. But when I click the No radio button, the model changes correctly but the html element does not become selected. If I click the No radio button again the html element then changes to it's correct selected state.
The example below is just part of a larger html page and controller but I have kept the Angular model structure the same because this may be where the issue is.
HTML:
<div ng-app="myApp">
<div ng-conroller="MyController">
<div>
<label>
<input type="radio" name="IsBeingPaid" ng-model="item.isBeingPaid" ng-checked="item.isBeingPaid" value="true"/>
Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="IsBeingPaid" ng-model="item.isBeingPaid" ng-checked="!item.isBeingPaid" value="false"/>
No
</label>
<p>{{item.isBeingPaid}}</p>
</div>
</div>
</div>
Controller:
var app = angular.module('myApp', [
'my.controllers'
]);
var controllers = angular.module('my.controllers', []);
controllers.controller('MyController', function($scope) {
$scope.item = {};
});
I have created this fiddle to demonstrate the issue.
http://jsfiddle.net/prajna78/Tdq9n/14/
What am I missing? It seems like such a simple thing.
There are a few problems with your code.
First, use ng-value instead of value in your radio button elements. This makes sure that the value you're binding to is a boolean (true) and not a string ("true"). Also, you don't need ng-checked (ng-model is sufficient).
<input type="radio" name="IsBeingPaidMinimumWage" ng-model="isBeingPaidMinimumWage" ng-value="true"/>
Also, you're binding to item.isBeingPaidMinimumWage, but your $scope variable is just isBeingPaidMinimumWage, so the initial value that you assign in your controller isn't reflected in the view.
Demo
I have a model returning in the storeLocations object with a isDefault value. if isDefault returns true, I wan't to set that radio button in the group as checked.
Not sure if I need to do a $each(data, function(index,value) and iterate through each object returned or if there's an easier way to do this using angular constructs.
Object:
storeLocations = [
{
... more values,
isDefault: true
}
]
Markup:
<tr ng-repeat="location in merchant.storeLocations">
<td>{{location.name}}</td>
<td>{{location.address.address1}}</td>
<td>{{location.address.address2}}</td>
<td>{{location.address.city}}</td>
<td>{{location.address.stateProvince}}</td>
<td>{{location.address.postalCode}}</td>
<td>{{location.address.country}}</td>
<td>{{location.website}}</td>
<td>{{location.zone}}</td>
<td><input type="radio" ng-model="location.isDefault" value="{{location.isDefault}}" name="isDefault_group"></td>
Use ng-value instead of value.
ng-value="true"
Version with ng-checked is worse because of the code duplication.
If you have a group of radio button and you want to set radio button checked based on model, then radio button which has same value and ng-model, is checked automatically.
<input type="radio" value="1" ng-model="myRating" name="rating" class="radio">
<input type="radio" value="2" ng-model="myRating" name="rating" class="radio">
<input type="radio" value="3" ng-model="myRating" name="rating" class="radio">
<input type="radio" value="4" ng-model="myRating" name="rating" class="radio">
If the value of myRating is "2" then second radio button is selected.
One way that I see more powerful and avoid having a isDefault in all the models is by using the ng-attributes ng-model, ng-value and ng-checked.
ng-model: binds the value to your model.
ng-value: the value to pass to the ng-model binding.
ng-checked: value or expression that is evaluated. Useful for radio-button and check-boxes.
Example of use:
In the following example, I have my model and a list of languages that my site supports. To display the different languages supported and updating the model with the selecting language we can do it in this way.
<!-- Radio -->
<div ng-repeat="language in languages">
<div>
<label>
<input ng-model="site.lang"
ng-value="language"
ng-checked="(site.lang == language)"
name="localizationOptions"
type="radio">
<span> {{language}} </span>
</label>
</div>
</div>
<!-- end of Radio -->
Our model site.lang will get a language value whenever the expression under evaluation (site.lang == language) is true. This will allow you to sync it with server easily since your model already has the change.
Ended up just using the built-in angular attribute ng-checked="model"
As discussed somewhat in the question comments, this is one way you could do it:
When you first retrieve the data, loop through all locations and set storeDefault to the store that is currently the default.
In the markup: <input ... ng-model="$parent.storeDefault" value="{{location.id}}">
Before you save the data, loop through all the merchant.storeLocations and set isDefault to false except for the store where location.id compares equal to storeDefault.
The above assumes that each location has a field (e.g., id) that holds a unique value.
Note that $parent.storeDefault is used because ng-repeat creates a child scope, and we want to manipulate the storeDefault parameter on the parent scope.
Fiddle.
Just do something like this,<input type="radio" ng-disabled="loading" name="dateRange" ng-model="filter.DateRange" value="1" ng-checked="(filter.DateRange == 1)"/>