Bind object's key to radio input's value in AngularJS - javascript

I'm having an object in my controller like the following:
$scope.colors = {
green: "Green",
yellow: "Yellow",
red: "Red"
};
I'm trying to create the radio inputs dynamically and then bind the value of the input to the object's key.
I'm trying something like this:
<label ng-repeat="color in colors">{{color}}
<input type="radio" ng-model="model.color" name="name" ng-value="{{color}}" ng-change="test()" />
</label>
but I can't make it work.
Here is my fiddle

You never define your model on the controller. I have updated your fiddle to do so: https://jsfiddle.net/Xsk5X/1380/
$scope.model = {"color":"test"};
I also added a <span> which displays the selected color to show it is working
I've added a new function and variable - $scope.createColors and $scope.colorsToBind.
The function will convert $scope.colors into an array of just the object keys, and then create a new array of Objects containing the key and value for that color, but as accessible fields; each will look like {key:"green", value: "Green"}. Once we have the array of these objects, the function will then set the value of $scope.colorsToBind to that array.
Your html is now using that new variable colorsToBind, and is displaying the value of each object but binding to the key of each one.

I managed to come with a cleaner solution.
<label ng-repeat="(key, value) in colors">
<input type="radio" ng-model="model.color" name="name" ng-value="key" /> {{value}}
</label>
here is the fiddle

you can do like this also :
<label ng-repeat="color in colors">{{color}}
<input type="radio" ng-model="colors" name="name" value="{{color}}" ng-change="test()" />
</label>
$scope.test = function() {
alert($scope.colors);
};
Jsfiddle

Related

How to concatenate values using Reactive Forms?

I want to concatenate 2 values into 1 label using Reactive Forms.
In this case i'm not using ngModel binding.
<label
id="identificationCode"
name="identificationCode"
formControlName="lblIdCode">______</label>
<input type="text"
id="reference"
name="reference"
formControlName="txtReference"
maxlength="250"
(change)="handleIdCode($event)">
<input type="text"
id="publicacion"
name="publicacion"
formControlName="txtPublicacion"
maxlength="250"
(change)="handleIdCode($event)">
I want to concatenate those 2 input text when user is writing and automatically reflect the value into the label. Is there any way like we do it with model binding without change event??
Use label to display the information. The label is not meant to bind with Reactive Form. If you need concatenate values to pass to API or for any use then try on TS. User cannot change the value of Label so there is no point to bind it, but just display the concatenated value.
Remove formControlName="lblIdCode" from your label and add for attribute.
<label>{{form.get('txtReference').value}} - {{form.get('txtPublicacion').value}}</label>
And concatenate on TS:
const lblIdCode = this.form.get('txtReference').value + this.form.get('txtPublicacion').value
The definition of label:
The HTML element represents a caption for an item in a user interface.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
Although given answers work fine. There could be another declarative approach which will take advantage of valueChanges observables of the input text. We can combine the input texts' valuechanges observables and map to the desired output i.e. concatenate the Reference + Publicacion like this:
Component.ts:
export class FormreactiveComponent implements OnInit {
lblIdCode$: Observable<string>;
form = new FormGroup({
txtReference: new FormControl(''),
txtPublicacion: new FormControl('')
});
constructor() { }
ngOnInit() {
const refCtrl = this.form.get('txtReference');
const pubCtrl = this.form.get('txtPublicacion');
this.lblIdCode$ = combineLatest(refCtrl.valueChanges.pipe(startWith(refCtrl.value)),
pubCtrl.valueChanges.pipe(startWith(pubCtrl.value)))
.pipe(map(([firstName, lastName]) => {
return `${firstName} ${lastName}`;
}));
}
}
Template:
<form name="form" [formGroup]="form">
<div class="form-group">
<label for="txtReference">Reference</label>
<input type="text" class="form-control" formControlName="txtReference"/>
</div>
<div class="form-group">
<label for="txtPublicacion">Publicacion</label>
<input type="text" class="form-control" formControlName="txtPublicacion"/>
</div>
<div class="form-group">
<label for="lblIdCode">{{lblIdCode$ | async}}</label>
</div>
</form>
Working example
One approach is that you can use reference variables to refer to controls within the template. Like so
<label
id="identificationCode"
name="identificationCode">{{reference.value + " - " + publicacion.value}}</label>
<input type="text"
id="reference"
name="reference"
formControlName="txtReference"
maxlength="250"
#reference>
<input type="text"
id="publicacion"
name="publicacion"
formControlName="txtPublicacion"
maxlength="250"
#publicacion>
The important parts are the #reference and #publicacion on each of the inputs. This links the controls to the variables.
You can then use these variables within an Angular interpolation block like {{reference.value + " - " + publicacion.value}}. You can combine the values however you want inside this block.
Try using the form values, just like you'd use in the .ts file.
<label
id="identificationCode"
name="identificationCode"
formControlName="lblIdCode">
{{form.value.reference + ' ' + form.value.publicacion}}
</label>
you can create a get property base of these values like in
componenpe ts
get referencePublicacionValues() : string {
return `${this.form.get(txtReference).value} ${this.form.get('txtPublicacion').value}`
}
template
<label
id="identificationCode"
name="identificationCode">
{{referencePublicacionValues}}
</label>
now you have the value in reference Publicacion Values property any change the value will reflect to the ui
you can't use formControlName directive on labels if you want to set
the formcontrol lblIdCode you can use form valueChanges
this.form.valueChanges.subscribe( () => {
this.form.get('lblIdCode').setValue(this.referencePublicacionValues,{emitEvent: false})
})
demo 🔥🔥

newly added item in ng-repeat is changing

i am trying to add a object in ng-repeat, as shown :
<div ng-controller="filtersController as datas">
<table>
<tr>
<th>Names</th>
<th>nos</th>
</tr>
<tr ng-repeat="data in datas.dataset">
<td>{{data.name}}</td>
<td>{{data.no}}</td>
</tr>
</table>
<input type="text" ng-model="models.name" placeholder="enter name" />
<input type="number" ng-model="models.no" placeholder="enter number" />
<input type="button" ng-click="datas.add(models)" value="click me!!!!" />
</div>
relevant javascript is :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (model) {
this.dataset.push(model);
$scope.$apply();
}}]);
Problem :
when i try to add new entries using textboxes, it will add first entry but after that i cannot add anymore entries in it, also the latest added entry changes when the values in the textboxes changes. Why is that?
Also if i change javascript like this :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (names, nos) {
this.dataset.push({ name: names, no: nos });
$scope.$apply();
}}]);
and HTML :
<input type="text" ng-model="name1" placeholder="enter name" />
<input type="number" ng-model="no1" placeholder="enter number" />
<input type="button" ng-click="datas.add(name1,no1)" value="click me!!!!" />
it adds as many entries i want and works as expected.
What is wrong with the first approach?
ng-model="models.name" creates an object on the scope named "models". Changes to models.name and models.no will change values within that object, but it's still a single object -- so datas.add(model) winds up pushing a reference to that single object onto this.dataset. Further changes to model will change that original object, which is already in this.dataset, because it's all references to the same object.
Your other approach works correctly because you're using separate primitives for ng-model="name1" and ng-model="no1". Here, each time datas.add(name1,no1) runs, it's pushing a newly constructed object onto this.dataset. Further changes to name1 and no1 now won't modify old data, because they're primitives, not object references, and each time you push to this.dataset you construct a new object out of those primitives.
When you push the model object that you pass to your add() method onto the array you are actually pushing the reference to that object and now Angular is going to use two-way data binding on it. What you need to do is first copy the model so it disassociates it from the ng-model directives in your HTML.
this.add = function(model) {
var newModel = angular.copy(model);
this.dataset.push(newModel);
}
Here's a Plunker: http://plnkr.co/edit/SdtSesYxb828yJyDFwws?p=preview

How can I concatinate a ng-model value with a value from a ng-repeat

I want to loop through an array of objects that I receive from a REST service and create a dynamic form using the ng-repeat directive.
This is my form with a rating directive (taken for the UI Bootstrap library)
<form name="categoryRatingFrom" data-ng-submit="updateCategories(catRatings) >
<div data-ng-repeat="cats in categories" class="form-group clearfix">
<label class="control-label">{{ cats.name }}</label>
<div class="no-outline"
data-rating
data-ng-model=" // Here I want to concatenate {{ cats.id }} with the ng-model name catRatings // "
data-max="6"
data-rating-states="ratingOptions.ratingStates"
data-on-hover="atmosphereRating.onHover(value)"
data-on-leave="atmosphereRating.onLeave()"></div>
</div>
<form>
I want to set the data-ng-model value using the object name that I pass when submitting and the ID of the current object tin my loop/array, however I don't seem to be able to do this. Should I do the concatenation in the controller on receiving the object array using a loop and then set the data-ng-model using a value from the ng-repeat nothing is passed to the controller when submitting the form (see my code below):
// loop through the object adding a ng-model name that we match in our form...
for (var i = 0, l = $scope.categories.length; i < l; i++) {
$scope.categories[i]['modelId'] = 'catRatings.' + $scope.categories[i].id;
}
I add the following to my HTML data-ng-model="cats.modelId" but nothing is passed to the controller when submitting - can any one help me with a solution or give me an answer to what I am doing wrong?
ng-model takes a variable, not a string value. I'm not sure what exactly you're trying to do but I would think it would be something like:
ng-model="catRatings[cats.id]"
However, I am not familiar with that directive so I'm not sure if it accepts a ng-model attribute.
I have created an example that will be referenced throughout the post. The following variables are declared within the scope as follows.
$scope.data = [
{
'id' : 0,
'name' : 'Tim'
},
{
'id' : 1,
'name' : 'John'
}
];
$scope.ratings = [ '5 stars', '2 stars' ];
When you are setting your model to 'catRatings' + {{ cats.id }} that means somewhere you are declaring a $scope.catRatings1, $scope.catRatings2, etc. Instead you should bind directly the the category object as follows.
<label ng-repeat="person in data">
<input type="text" ng-model="person.name">
...
</label>
or bind to an array with a corresponding index
<label ng-repeat="person in data">
...
<input type="text" ng-model="ratings[$index]">
</label>
or bind to an array using the id...
<label ng-repeat="person in data">
...
<input type="text" ng-model="ratings[person.id]">
</label>

Using an array of ng-model

I am new to AngularJS. In my scenario, the user has to create a mcq question. The question has 4 default option and one of the options is correct. Now the user who is teacher can give greater or less then 4 options for the question. So its a variable number of options. If hard code the input as follow
<input name = "input0" type = "text", class = "form-control" ng-model = "input_0" required>
<input name = "input1" type = "text", class = "form-control" ng-model = "input_1" required>
and so on it works good. I want to use dynamic solution here, so it does not matter how many options the teacher provide.
What I was trying to do is
$scope.mcq_options = [$scope.input_0,$scope.input_1 ...]
use ng-repeat in html template and do something like
<div ng-repeat = "input in mcq_options">
<input name = "input1" type = "text", class = "form-control" ng-model = "input" required>
For removing splice entry from array
For adding more push entry in array
The solution is quite straightforward (Associated PLUNKER):
1 Create an empty array that you may store all your options, in your controller.
var inputArray = $scope.inputArray = [];
[2] Create a function to add new options.
$scope.addNewOption = function() {
inputArray.push('');
};
[3] Create another function to splice an option entry that accepts the index of an option to remove.
$scope.removeOption = function(index) {
inputArray.splice(index, 1);
};
[4] Your view can be something like this:
<form name="form" novalidate>
<div ng-repeat="input in inputArray track by $index" ng-form="subform">
<input name="input" type="text" ng-model="inputArray[$index]" required> <button ng-click="removeOption($index)">Remove</button>
<br>
<span ng-show="subform.input.$error.required">This field is rqeuired</span>
</div>
<button ng-click="addNewOption()">Add New Option</button>
</form>
Note:
The track by $index in the ng-repeat directive helps in avoiding duplicate values error.
The ng-form directive helps you in validating each models that is created in every ng-repeat iteration.
Instead of using the input value in the ng-repeat directive, use its direct reference by using the ng-repeat's $index property. If you dont't do this, changes in the inputArray may affect the current ngModel reference of your inputs. e.g. adding or removing options will give you weird behaviours.

Hide and Show elements based on values in Array in AngularJS

I have the following HTML code:
<div ng-controller="DemoController">
<label class="list-group-item" ng-repeat="option in DesignOptions">
<input type="radio" name="optionsRadios" value="{{option[0]}}" />
{{option[1]}}</label>
<label class="list-group-item" ng-repeat="option in StyleOptions">
<input type="checkbox" value="{{option[1]}}">
{{option[2]}}
</label>
</div
And I have the following AngularJS code:
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('DemoController', function ($scope) {
var json = '{"Table":[[4,"Full"],[5,"Half"]],"Table1":[[4,1,"Elbow Patch"],[5,2,"Roll Up"]]}';
var obj = $.parseJSON(json);
$scope.DesignOptions = obj.Table;
$scope.StyleOptions = obj.Table1;
});
</script>
This gives me the following result:
Now, I need to display Elbow Patch checkbox only when Full radio button is selected. And Roll Up when Half radio button is selected. This is because, if you see obj.Table array, it has id of '4' for Full and obj.Table1 has id of '4' for Elbow Patch and so on.
I tried Angularjs - showing element based on a presence of id in array but could not modify it to work in my case as my array is very different.
Add a new property to your controller which will store the selected design option:
$scope.designOption = 4; // default to Full
Add the binding to this property in the view:
<input ng-model="$parent.designOption" type="radio" value="{{option[0]}}" />
Add an ng-show directive to the checkbox label:
<label ng-repeat="option in StyleOptions" ng-show='option[0] == designOption'>
I've removed the class and name attributes from the element just to make the code clearer.
NB Need to reference the $parent scope on the radio input as the ng-repeat directive will create a scope for each repeated element and javascript prototypical inheritance rules means that using just 'designOption' will create a designOption property on the child scope and not use the one in your controller.

Categories