Using an array of ng-model - javascript

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.

Related

Disable input with dynamically generated fields in Angular

Imagine this:
http://jsfiddle.net/wcuuj8do/9/
My current code:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.rowData = [];
$scope.addRow = function(title, number)
{
$scope.rowData.push({
'title': title,
'number': number
});
};
$scope.addRow('Car', '1200');
$scope.addRow('Car','');
}
When i type "Car" inside first input (T1) and then type some text to input (N1) i want angular to check each T# input if has same value as (T1). If has disable (or readonly) all N# inputs related to currently checked T# input.
Afterwards when i remove duplicated values from T# fields, related T# fields should be returned to default input states (remove disable / readonly)
This should work by adding new dynamic inputs as seen in fiddle.
You should create a method, that will do the checking part. This method will should be bound to blur or change event on T# inputs, depends on what you want.
The method will check for duplicity, and if found, mark the object, e.g. add new property disabled: true. This property will be then used in the template on N# fields via ng-disabled directive.
Here is your update fiddle: http://jsfiddle.net/wcuuj8do/10/
Note the new method $scope.checkDuplicity and new binding:
<tr ng-repeat="(key, item) in rowData">
<td>T{{ ($index+1) }}: <input type="text" ng-model="rowData[key].title" ng-change="checkDuplicity(key)" value="" style='margin-bottom:15px' /></td>
<td>N{{ ($index+1) }}: <input type="text" ng-model="rowData[key].number" value="" style='margin-bottom:15px' ng-disabled="rowData[key].disabled" /></td>
</tr>

Javascript set value for Form obeject Array

I am looking to add data to a form object which is an array.
This works fine:
<input type="text" name="object" value="">
<script>document.form.object.value = "value";</script>
But when the object is an array it's not working:
<input type="text" name="object[]" value="">
<script>document.form.object[0].value = "value";</script>
The value of the object is not changing.... Any idea?
I would like to loop the script so I need to create an array. Didn't find any solution...
Per example, I would utilize document.form.elements['object[]'].value = "value". Otherwise, if you intended on having multiple form elements with the same name (multiple inputs with object[], and iterate via the collection, can use the following:
var myForm = document.form;
var myControls = myForm.elements['object[]'];
for (var i = 0; i < myControls.length; i++) {
var aControl = myControls[i];
}
The example provided, in your code, the name provided is not perceived as an array.
The attribute value "object[]" is just a string to JavaScript -- it does not interpret that as an array. However, when brackets appear in a name, you cannot use it any more in the dot-notation, but must write:
document.form["object[]"].value = "value";
<form name="form">
<input type="text" name="object[]" value="">
</form>
If you have more than one element with name="object[]", then the above will only target the first one of these. To set the value of all those elements, you must loop. This you can (for instance) do with the elements property and Array.from to iterate over those elements:
Array.from(document.form.elements["object[]"], function(elem) {
elem.value = "value";
});
<form name="form">
<input type="text" name="object[]" value="">
<input type="text" name="object[]" value="">
</form>
For those using IE: replace Array.from with [].map.call

All checkboxes getting checked even if one is checked

I'm making a basic todo app in angularjs.
Here's the code that's used to create a new todo with a checkbox
<div class="container" ng-controller = 'controller' >
<h3>Enter Todo </h3> <input type="text" ng-model = 'new'>
<button ng-click = 'add();' ng-disabled="status();">Add</button>
<div ng-repeat = "i in todo" >
<input type="checkbox" ng-model = "todo.done" /> <label>{{i.name}}</label>
</div>
</div>
The problem is that all the checkboxes get checked even when I check just one
Here's my controller module
todoApp.controller('controller',['$scope',function($scope){
$scope.new = '';
$scope.todo = [];
$scope.add = function(){
$scope.todo.push({name : $scope.new, done: false});
console.log($scope.todo);
$scope.new = '';
};
$scope.status = function(){
return !(/\S+/.test($scope.new));
};
ng-model = "i.done"
should solve the problem. In your version ng-model = "todo.done" todo is an array and angular just creates a property on the fly, when it's used for the first time. This way all of your checkboxes are connected to this property, that's why checking one checkbox affects all of them.
Because the list of the checkbox are sharing the same model. You can create a custom directive or you can use checklist-model directive.
Demo URL:
https://vitalets.github.io/checklist-model/

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>

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