Two way binding in *ngFor on Array of elements(non-object) - arrays

I have a simple array tags = []; and I'm pushing blank element on addNew() using this.tags.push("");. It creates blank element in that array.
Now, I'm using this array to add multiple items to be added from the input
<div class="row tagEntry" *ngFor="let tag of tags, let i = index">
<div class="col-md-10">
<input type="text" name="tag-{{i}}" placeholder="Tag Name" [(ngModel)]="tag" class="form-control">
</div>
<div class="col-md-2">
<button class="btn btn-block btn-danger" (click)="removeTag(i)"><i class="fa fa-trash"></i></button>
</div>
</div>
My TS file:
todoDesc: string = '';
tags = [];
ngOnInit() {
}
addTag () {
this.tags.push("");
console.log(this.tags)
}
removeTag (index) {
this.tags.splice(index, 1);
}
addTodo () {
console.log(this.todoDesc, this.tags)
}
Issue here is, it is not binding the input and array two way. When I log the array after updating inputs, the array displays items with all blank value.
How to two way bind the array of elements in Angular 5 using *ngFor?

The issue is with Array whose changes are not being detected through reference. You can make the following changes -
html
<div class="row tagEntry" *ngFor="let tag of tags; let i = index; trackBy:trackIndex">
<div class="col-md-10">
<input type="text" placeholder="Tag Name" [(ngModel)]="tags[i]" class="form-control">
</div>
<div class="col-md-2">
<button class="btn btn-block btn-danger" (click)="removeTag(i)"><i class="fa fa-trash"></i></button>
</div>
</div>
ts
Add the following function in ts file. This function is very important otherwise the array will re-build the complete UI on any change and you will loose the focus from the elememt.
trackIndex(index,item){
return index;
}
Here is the working copy - https://stackblitz.com/edit/angular-pzdw6s

Related

AngularJS 1.x: Dynamically created multiple File Upload fields

You have to excuse me, I do not use Angular that often. I need to implement a solution in which I can upload files from dynamically generated input fields.
The catch is that I cannot use any directives (this is due to the framework I am using in which AngularJS is implemented).
My current code for creating fields:
$scope.addNewFile = function() {
var newFileNo = $scope.files.length + 1;
$scope.files.push({'id': 'file' + newFileNo});
};
$scope.removeFile = function() {
var lastFile = $scope.files.length - 1;
$scope.files.splice(lastFile);
};
And here is the code for uploading files (not correct, because of static id and name values:
$scope.uploadFile = function() {
var file = document.getElementById('file').files[0],
r = new FileReader;
r.onloadend = function (e) {
var data = e.target.result;
console.log('data');
console.log(data);
// http.post
};
r.readAsArrayBuffer(file);
};
And here is my HTML:
<div data-ng-repeat="file in files">
<div class="row top-buffer">
<div class="col-lg-10">
<input type="file"
ng-model="file.name"
name="file"
class="form-control"
id="file">
</div>
<div class="col-lg-2 text-right">
<div class="btn btn-primary" ng-click="uploadFile()">Upload</div>
</div>
</div>
</div>
<div class="row top-buffer">
<div class="col-lg-11"></div>
<div class="col-lg-1 text-right">
<div class="glyphicon glyphicon-plus" aria-hidden="true" ng-click="addNewFile()"></div>
<div class="glyphicon glyphicon-minus" aria-hidden="true" ng-click="removeFile()"></div>
</div>
</div>
I can't really understand how I can get the values of dynamically generated file fields... any help appreciated!
There is no ng-model for <input type=file, but you can achieve what you want setting the id dynamically with ng-attr:
<div ng-repeat="file in files">
{{file.id}}:
<input type="file"
ng-attr-name="{{file.id}}"
ng-attr-id="{{file.id}}">
</div>
On uploadFile() you can post them:
$scope.uploadFile = function() {
$scope.files.forEach(function(file) {
// $http.post: document.getElementById(file.id).files[0])
});
};
Example on Fiddle: https://jsfiddle.net/virgilioafonsojr/92nw4j4f/1/
I hope it helps.
Writing an uploader properly is a very difficult task. A major problem with an upload component/directive is getting it to work in Internet Explorer; because it can only be used once in IE. Another problem is that IE will not let you programmatically "press" the button, it must be a real mouse click.
There are upload libraries out there for AngularJs, just google. However if you are intent on doing it yourself then you can use my git repo as an example of how it should be done. Just use the jwtUploader and jwtUploaderButton directives.
https://github.com/smylydon/SEPE-7WCM0033-0206-FRONTEND/tree/master/client/app

Removing a value from an array and moving them to another Array

I have a dropdown values from $scope.role from there I am able to select and remove the respective value from that dropdown and moving the values to $scope.multiRoles array . Which I am also displaying as tags .Which can be find in AddRole()
Now when I click close button in the tag, I am calling removeRoles() , where I am splicing the array which I am selected. But I need to splice them and move them back to $scope.role. Till removing value it works fine , but I am unable to move it back to $scope.role as well. I need atleast one tag should be remained, I can not delete all of them.
HTML:
<div class="row newRow">
<div class="form-group fields col-sm-2 col-xs-4">
<label>ROLE *</label>
<select name="role" class="form-control1 drop2" required ng-model="model.role" placeholder="select">
<option value='' disabled selected>Select</option>
<option ng-repeat="item in role track by $index" value="{{item}}">{{item}}</option>
</select>
</div>
<div class="form-group col-sm-2 col-xs-4">
<button ng-click="AddRole($index)">Click to Add your Role</button>
</div>
</div>
<div ng-if="rolesAdded" class="col-sm-12 col-xs-12">
<span class="tag label tagStyle newStyling" alue="data" ng-repeat="data in multiRoles track by $index">
<span>{{data}}</span>
<a><i ng-click="removeSelection()"class="remove glyphicon glyphicon-remove-sign "></i></a>
</span>
</div>
JS:
$scope.AddRole = function(index) {
debugger;
if ($scope.model.role !== undefined) {
$scope.multiRoles.push($scope.model.role);
var index = $scope.role.indexOf($scope.model.role); //just find the right index which is the selected option in dropdown.
$scope.role.splice(index, 1);
console.log($scope.role);
$scope.model.role = $scope.role[0];
}
$scope.rolesAdded = true;
};
$scope.removeRoles = function(index) {
debugger;
if ($scope.multiRoles !== null) {
$scope.multiRoles.splice(index, 1);
}
};
In your html import data to your removeRoles(data) function:
<button ng-click="removeRoles(data)">
<i class="remove glyphicon glyphicon-remove-sign"></i>
</button>
In your controller:
$scope.removeSelection = function(data) {
$scope.multiRoles.splice($scope.multiRoles.indexOf(data), 1);
$scope.role.push(data);
$scope.rolesAdded = $scope.multiRoles.length === 0 ? false : true;
};
Code here. Hope this helps.

how to remove the value of an input field using angularjs

<div class="info-block" ng-app="">
<div ng-controller="Note">
<div class="checkbox">
<label>
<p><b>Primary Publication: </b>
{{ form_widget(form.input_ppubs, { 'attr': {'class': 'valOption'}}) }}
</p>
</label>
</div>
<div ng-repeat="item in items">
<input type="text" placeholder="new input" ng-model="item.primaryPub">
</div>
<button type="button" ng-click="add()">New Item</button>
</div>
I am trying to retrieve the value of the html input field and remove it from input upon a button click
I am able to get the code, but I don't know how to remove the code.
var Note = function($scope){
$scope.items = [];
$scope.add = function () {
//angular way of implementing document.getElementByID();
pub1 = angular.element('#form_input_ppubs').val();
$scope.items.push({
primaryPub: pub1
});
};
}
You don't have to retrieve your items like this. It's ugly and not the angular way. angular.element('#form_input_ppubs').val();
Instead, simply reference it in your input using ngModel.
Declare it in your scope.
$scope.inputItem = null;
HTML
<input ng-model="inputItem " />
Use it in your function:
$scope.addItem = function(item) {
$scope.items.push(item);
//reset the model
$scope.inputItem = null;
}
Call it using ng-click
<button type="button" ng-click="addItem(inputItem)">Add Item</button>
If you do:
console.log($scope.items);
You should see an entry for primaryPub, the model for your input. Then you can target it by nulling the model, so:
$scope.items.primaryPub = null;
However you're using this inside an ng-repeat:
<div ng-repeat="(i, item) in items">
<input type="text" placeholder="new input" ng-model="items[i].primaryPub">
</div>
So your console.log (if you have more than one item in 'items') should show an array-like structure for primaryPub.

anjularjs push value to nested ng-repeat

I have an ng-repeat nested within another ng-repeat. I want to push some values to the second array when the button is clicked
<div ng-repeat="vehicle in vehicleList">
<div>{{vehicle.number}}</div>
<input name="addName" value="add" ng-click="addVehicle(vehicle)"
<div ng-repeat="categoryList in vehicle.category">
<div>{{categoryList.name}}</div>
</div>
</div>
I tried following code but it's not working
$scope.vehicleList=[];
$scope.addVehicle = function(){
$scope.vehicleList.push({
category:'car'
});
}
Can anyone help me on this. Thanks
<div ng-repeat="vehicle in vehicleList">
<div>{{vehicle.number}}</div>
<input name="addName" value="add" ng-click="addVehicle($index, vehicle)">
<div ng-repeat="categoryList in vehicle.category">
<div>{{categoryList.name}}</div>
</div>
</div>
$scope.vehicleList = [];
$scope.addVehicle = function(index, vehicle) {
$scope.vehicleList[index].category.push({
name: vehicle
});
};

How to get Angular to have a click event bring part of the dom with it to the function?

I am creating a small practice to-do list app with Angular. I have created a form for creating new tasks and they are updating fine. I have a button generate for every task that is titled "remove". I'd like to make the button remove specifically that task from the todo list. Here's the html code:
<div ng-controller="todoListController">
<ul>
<li ng-repeat="task in taskList">
<p>
{{task.name}}: {{task.description}}
<button ng-click="killtodo()"> Remove </button>
</p>
</li>
<hr>
<h3> Display: </h3>
<li>
<p>
{{task}}: {{taskDescription}}
<p>
</li>
</ul>
<hr>
<form ng-submit="addto()">
<input type="text" ng-model="task" placeholder="Enter a task name.">
<input type="text" ng-model="taskDescription" placeholder="Enter the description.">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
Javascript code:
function todoListController($scope) {
$scope.taskList = [
{"name": "Task List",
"description": "Make a Task List."}
];
$scope.addto = function() {
$scope.taskList.push({name:$scope.task, description:$scope.taskDescription});
$scope.task = '';
$scope.taskDescription = '';
};
$scope.killtodo = function(name) {
};
}
Any suggestions would be great, thanks.
You have access to a variable called $index while using an ng-repeat so you can do something like this:
<li ng-repeat="task in taskList">
<p>
{{task.name}}: {{task.description}}
<button ng-click="killtodo($index)"> Remove </button>
</p>
</li>
And then in your controller:
function todoListController($scope) {
//other code
$scope.killtodo = function($index){
$scope.taskList.splice($index, 1);
}
}
There's more description available in the docs at the top (including other variables you can use): http://docs.angularjs.org/api/ng.directive:ngRepeat

Resources