get values from each ng-repeat input elements - angularjs

I have the following code where I have added 5 input texts via ng-repeat and now am trying to fetch each of the input element's values. I am hitting some roadblock, so please help me out on how to fetch individual values.
HTML:
<form class="quick-add-form" method="post">
<label class="quick-add-label">Enter the catalog number and quantity</label>
<div class="quick-add">
<div class="catalog-list">
<label class="catalog-number-label" for="catalog-number-input">Catalog Number:</label>
<div ng-repeat="values in catalogNumber(number) track by $index">
<input id="catalog-number-input-{{$index}}" class="catalog-number-input"
type="text" ng-model="catalog.description" validate-input>
</div>
</div>
<div class="qnty-list">
<label class="pdt-qnty-label" for="pdt-qnty-input">Quantity:</label>
<div ng-repeat="values in catalogNumber(number) track by $index">
<input id="pdt-qnty-input-{{$index}}" class="pdt-qnty-input" type="number" pattern="[0-9]*"
inputmode="numeric" min="1" max="999" ng-model ="catalog.quantity" name="qty"
maxlength="3">
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-danger cta-button quick-add-btn"
ng-click ="quickAdd(catalog)">Add to List</button>
</div>
</form>
and controller:
$scope.catalog = {};
$scope.catalogNumber = catalogNumber;
$scope.initializeModal = initializeModal;
$scope.quickAdd = quickAdd;
function catalogNumber(num) {
return new Array(num);
}
function quickAdd(val) {
console.log(val);
}
function init() {
$scope.number = 5;
$scope.catalog.quantity = 1;
}
I am pretty sure its because of indexing and which is why by adding value in one input, its adding same value in all at once. But I am not sure how/where to use the $index properly to achieve the result.
Thanks.

Perhaps a better way would be to use the model? For example:
<div ng-repeat="values in catalogNumber(number) track by $index">
<input
id="catalog-number-input-{{$index}}"
class="catalog-number-input"
type="text"
ng-model="catalog.description[$index]"
validate-input>
</div>
Now in your controller:
catalog.description = [];
Then you have an array of objects, which you can perform your math on:
var total = _.sum(Object.values(catalog.description));
Edit: I had forgot I was using Lodash/Underscore there, but if you wanted to use standard JS you could use a standalone method, or reduce etc.,

Related

Angular - Binding data to dropdownlist never clears out previous items on updating (keeps old items plus add new)

Hopefully a simple Angular binding question.
I have a TextBox on my Angular 4 [x.component.html] that has an (input) event tied to it, so each time the user types in a character into the textbox it reaches out to a method in the [x.component.ts] typescript file - which then retrieves data from a service to populate a local array of data in the typescript file - which is bound to a dropdownlist on the [x.component.html]. This process functionally works fine.
However the problem I am having is that when it updates the data in the local array, it is not clearing out the previous data in the dropdown, so the dropdown list ends up getting populated with lots of duplicate data. Has anyone experienced this same problem before? The local array of items seems to have the correct number of items in it, it just seems to keep populating the dropdownlist with the items without ever clearing the previous.
Thanks so much for your help.
Please note I have cut the code down to only the relevant parts for brevity.
[x.component.ts]
export class CreateComponent {
testService: TestService;
valueList: testValue[];
constructor(testService: TestService) {
this.testService = TestService;
}
ngOnInit() {
}
FieldChanged(event: any, controlNameToPopulate: string){
if (event.target.value.length >= 9) {
this.testService.getRecords(event.target.value).subscribe((data: any[]) => {
if (data.length != 0){
document.getElementById(controlNameToPopulate).hidden = false;
//this.valueList.slice();
this.valueList = data;
}
});
}
}
}
[x.component.html]
<div class="row">
<div class="col-md-6">
<nb-card>
<nb-card-header>Testing with Updating dropdownlist</nb-card-header>
<nb-card-body>
<form [formGroup]="formGrp">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Quick search</label>
<div class="col-sm-9 input-group">
<input type="text" class="form-control input-sm" style="width: 50%" id="txtbx_ValTest" formControlName="testVal" (change)="FieldChanged($event, 'ddValuesToUpdate')">
<select id="ddValuesToUpdate" class="btn btn-secondary dropdown-toggle" style="width: 50%" hidden>
<option *ngFor="let val of valueList" [value]="val.id">{{val.id}}</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button type="submit" class="btn btn-success pull-right">Add</button>
</div>
</div>
</form>
</nb-card-body>
</nb-card>
</div>
</div>

How to repeat duplicate objects using ng-repeat in AngularJs?

This is my code.
$scope.data=[];
$scope.data=[{"label":"name","type":"string"},{"label":"email","type":"string"}];
$scope.addFields = function (field) {
$scope.data.push(field);
};
This is my html:-
<div ng-repeat="eachItem in data">
<input type="button" value="add" ng-click="addFields(eachItem)"/>
<label>{{eachItem.label}}</label>
<input type="text" ng-model="fieldValue"/>
</div>
when i click add button push one more object into $scope.data array like
$scope.data=[{"label":"name","type":"string"},{"label":"email","type":"string"},{"label":"name","type":"string"}];
In the above i got an error
angular.min.js:102 Error: [ngRepeat:dupes] http://errors.angularjs.org/1.3.14/ngRepeat/dupes?p0=nestedField%20in%20fie…%2C%22type%22%3A%22string%22%2C%22%24%24hashKey%22%3A%22object%3A355%22%7D
at Error (native)
I have duplicate objects after adding. because i want to repeat label names using ng-repeat in angularjs.First i have output like this
OutPut:-
name textbox
email textbox
After add button click Output:-
name textbox
email textbox
name textbox
use track by $index
var app = angular.module("app",[])
app.controller('ctrl',['$scope', function($scope){
$scope.data=[];
$scope.data=[{"label":"name","type":"string"},{"label":"email","type":"string"}];
$scope.addFields = function (field) {
$scope.data.push(field);
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div class="item item-checkbox">
<div ng-repeat="eachItem in data track by $index">
<input type="button" value="add" ng-click="addFields(eachItem)"/>
<label>{{eachItem.label}}</label>
<input type="text" />
</div>
</div>
Use track by for this purpose.
<div ng-repeat="eachItem in data track by $index">
<input type="button" value="add" ng-click="addFields(eachItem)"/>
<label>{{eachItem.label}}</label>
<input type="text" ng-model="eachItem.value" />
</div>
You also able to use track by with your custom filed, like id, or whatever
Important: It's better to use track by in each ng-repeat, cause it's improve ng-repeat's performance (read more).
But avoid to use track by in ng-options and other cases when you use select as .. for ... construction (read more)
JsFiddle here
You have to ensure that items in the array have an unique key. If that is not possible you can use track by $index in the ng-repeat.
Check the details here

ng-models name collision inside ng-repeat

http://plnkr.co/edit/2UFfaG?p=preview
I used this sample code to build a simple app and I noticed that the edit function doesn't work when you are using ng-models that are repeated in a loop. I know this, because I tried using ng-models outside of the ng-repeat loop and it worked perfectly. So when you have two instances of ng-models with the same name, you get a blank data back when you try to get the values back from the view.
This is my view:
<ul ng-repeat="notes in notes">
<li>
<span ng-hide="editing" ng-click="editing = true">{{note.name}} | {{note.content}}</span>
<form ng-show="editing" ng-submit="editing = false">
<label>Name:</label>
<input type="text" ng-model="name" placeholder="Name" ng-required/>
<label>Content:</label>
<input type="date" ng-model="content" placeholder="Content" ng-required/>
<br/>
<button class="btn" ng-click="edit(note.id)">Save</button>
</form>
</li>
</ul>
This is my edit method:
$scope.edit = function (id) {
var note = notesRef.child(id);
var newNote= {
name : $scope.name,
content : $scope.content
}
};
note.update(newNote);
};
When I refer to a ng-model inside of ng-repeat, I can only get the value null for some reason. I get the correct value when I refer to ng-models outside of the ng-repeat for some reason.
How do we solve this problem? What's the simplest solution?
The problem is that the item belongs to the scope of the repeat.
If you changed your ng-model to:
<ul ng-repeat="notes in notes">
<li>
<span ng-hide="editing" ng-click="editing = true">{{note.name}} | {{note.content}}</span>
<form ng-show="editing" ng-submit="editing = false">
<label>Name:</label>
<input type="text" ng-model="note.name" placeholder="Name" ng-required/>
<label>Content:</label>
<input type="date" ng-model="note.content" placeholder="Content" ng-required/>
<br/>
<button class="btn" ng-click="edit(note)">Save</button>
</form>
</li>
</ul>
Where it's now note.name / note.content.
Then instead of padding the note.id to the edit button, you pass in the entire note i.e ng-click="edit(note)"
Then your controller will get passed the entire note.
$scope.edit = function (note) {
// send note to server via $http, changes to `note` are already made directly to the note itself
};
Hope that makes sense.
it should be like this. As we know ng-repeats directive create their own new scope.
bday.editing
<ul ng-repeat="bday in bdays">
<li>
<span ng-hide="bday.editing" ng-click="bday.editing = true">{{bday.name}} | {{bday.date}}</span>
<form ng-show="bday.editing" ng-submit="bday.editing = false">
<label>Name:</label>
<input type="text" **ng-model="bday.name"** placeholder="Name" ng-required/>
<label>Date:</label>
<input type="date" **ng-model="bday.date"** placeholder="Date" ng-required/>
<br/>
<button class="btn" type="submit">Save</button>
</form>
</li>
</ul>
and here what I understand from your question is that you want to edit only the item on which you have click. this is the solution for the same.
One more solution for the same problem is that create a new function that take one argument that is "bday". make edit true only for this item and set editing false for all others element. this solution is for that case if user doesn't submit the form and click on other item.

Select at least one checkbox validation

I have the array of checkboxes from which I want to select at least one checkbox and want to display the validation error
How can I do that? Here is my code
<div class="form-group" ng-class="{ 'has-error' : submitted && form.field.$invalid }">
<div class="col-md-12"><label for="field" >Select at leat one</label></div>
<div class="col-md-2" data-ng-repeat="i in [1,2,3,4]">
<label><input type="checkbox" name="field[$index]" value="{{i}}" data-ng-model="form.field[$index]" required/>Choice {{i+1}}</label>
</div>
<div ng-show="submitted && form.field.$invalid" class="help-block">
<p ng-show="form.field.$error.required">Please select at least one choice<p>
</div>
</div>
</div>
You can use native javascript functions to iterate over your array. First that come in mind are Array.prototype.some() and Array.prototype.filter().
With some, you can determine if one item in your array validates true and with filter you can find all the elements that pass a custom expression and check if the newly created array has a certain length.
E.g.:
var atleastOne = this.form.field.some(function(element) {
return element;
});
if(atleastOne) {
window.alert("validated with some");
}
var filtered = this.form.field.filter(function(element) {
if(element) { return element; }
});
if(filtered.length > 0) {
window.alert('validated with filter');
}
You can flag the variables that show the errors based on the results of your validation. A working demo can be found here.
You can create your checkbox list object array properly then bind it. Please find the demo
<div ng-controller="ChckbxsCtrl">
<div ng-repeat="chk in items">
<div style="width:500px;height:100px;border:1px solid #000;">
<B>{{chk.group}}</B><br>
<label ng-repeat="vale in chk.values">
<input type="checkbox" ng-model="vale.val" />
{{vale.label}}</label>
</div>
<span ng-show="chk.isValid">Select at least one option from {{chk.group}}</span>
</br> </div>
</br>

Ng-model does not update view

I have searched thoroughly everywhere and can't seem to find the solution to my problem.
I am trying to make a form to fill in to create new task object onto projects on the website like a scrum backlog or something in Angular and angular-ui.
I use Angular-UI for typeahead functionality for when adding team members to a task, so available members on a project pop-up. I have seen guys with similar problems, but nothing seems to solve it for me. Below is the HTML and the controller. Sorry for the long markup, but I suspect it has to do something with nesting the controllers and mixing the scopes, so I'm including everything relevant.
<div ng-controller="SubmitCreateTaskController">
<div class="modal fade" id="taskModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title" id="myModalLabel">New Task</h4>
</div>
<div class="modal-body">
<form role="form" method="post" name="create-task">
<div class="form-group">
<label for="taskname">Task Name</label>
<input type="text" class="form-control" id="taskname" ng-model="taskForm.taskName" placeholder="Task Name">
</div>
<div class="form-group">
<label for="taskdesc">Description</label>
<textarea class="form-control" id="taskdesc" size="3" ng-model="taskForm.taskDescription" placeholder="Enter a short description here..." rows="2"></textarea>
</div>
<div class="form-group">
<label for="assigneddate">Assigned Date</label>
<input type="text" class="form-control" id="assigneddate" ng-model="taskForm.assignedDate">
</div>
<div ng-controller="TypeAheadController">
<div class="form-group">
<label for="contributors">Add Contributors</label>
<input id="contributors" type="text" class="form-control" ng-model="contrib.selected" typeahead="member for member in contrib.stream_members | filter:$viewValue"
typeahead-editable="false" typeahead-on-select="contrib.onSelect($item)">
</div>
<div class="form-group">
<label for="users">Contributors:</label>
<textarea class="form-control" disabled="disabled" id="users" ng-model="contrib.entered" rows="1"></textarea>
</div>
</div>
<div class="form-group">
<label for="taskcomments">Comments</label>
<textarea class="form-control" id="taskcomments" ng-model="taskForm.comment" placeholder="Comments" rows="2"></textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" ng-click="taskForm.submit()">Add Task</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>
</div>
Controllers:
.controller('SubmitCreateTaskController', ['$scope', '$http', function($scope, $http) {
$scope.taskForm = {};
$scope.taskForm.taskName = '';
$scope.taskForm.taskDescription = '';
$scope.taskForm.assignedDate = new Date();
$scope.taskForm.contributors = [];
$scope.taskForm.comment = '';
$scope.taskForm.submit = function(item, event) {
var formData = {name: $scope.taskForm.taskName,
description: $scope.taskForm.taskDescription,
status: 'open',
assigned: $scope.taskForm.assignedDate,
completed: null,
contributors: $scope.taskForm.contributors,
comments: [{body: $scope.taskForm.comment,
user: 'RACHE User',
date: new Date()
}]
};
var postURL = '../create_task/' + $scope.stream_name;
$http.post(postURL, formData)
.success(function(){
taskForm = {}; // empty the form of previous input values
alert('New Task Created for ' + $scope.stream_name);
})
.error(function(res) {
alert(res.data);
});
};
}])
.controller('TypeAheadController', ['$scope', '$http', function($scope, $http) {
$scope.contrib = {};
$scope.contrib.selected = '';
$scope.contrib.stream_members = undefined;
$scope.contrib.entered = [];
$http.get('/stream_members/' + $scope.stream_name)
.then(function(res) {
$scope.contrib.stream_members = res.data.stream_members;
});
$scope.contrib.onSelect = function($item) {
$scope.contrib.selected = '';
$scope.contrib.entered.push($item);
console.log($scope.contrib.entered);
};
This last bit is the important part in 'TypeAheadController'. I am trying to add the poped-up team members to a textarea just below which would be sent to the DB later. That is why I have bound the actual typeahead input box to 'contrib.select' and the "display/post" box to 'contrib.entered', so the search can be continued after adding one member, the search box is cleared, selected member is added to box below, new search can begin. In the mark up I call the last (onSelect) function in the typeahead-on-select callback. This so that I can clear the input box and another member can be easily added without deleting manually. IN this function the entered member should be appended to the array that is bound to below "display/post" box. This happens, since the console log shows good value, the search/input field gets cleared, so the function gets called as well.
The view does not get updated with the updated array bound to display box however. I have tried EVERYTHING. I have googled around and found it here that dot notation needs to be used as Angular can't update with primitives, but this hasn't helped either.
Everything works fine, the logic is good, values are good when I print them out, pop-up works and clears up as expected, but The damn textarea under it does not get updated.
Any suggestions? I have been stuck on this for almost a day now and I am really frustrated by this. I would be ever so greatfull for any help!
Thanks guys in advance!
Textarea do use the value attribute, your code isn't working because contrib. entered isn't between the tags, but because you can not bind the textarea to an array (it needs to bind to a string).
look at this plunker, if you do this in your html:
<textarea class="form-control" disabled="disabled" id="users" rows="1" ng-model="contrib.enteredString"></textarea>
and add this line at the end of your typeahead controller:
$scope.contrib.enteredString = $scope.contrib.entered.toString();
then the text area will update
Textareas don't use the ng-value as their value is contained within the tag. Remove the ng-model and put the value between the tags.
<textarea class="form-control" disabled="disabled" id="users" rows="1">{{contrib.entered}}</textarea>

Resources