Programmatically inserting array values in Angular JS - javascript

Update: I was able to solve it by changing how I was calling the push method. Refer to the inline comments in the snippet. Thanks SO for the help. Any comments / thoughts on why this is not a good idea would be highly appreciated.
I have an Angular JS array that is bound to my UI. When I add an item to it via the UI, it works fine and my UI is updated with the newly added item. So, this code works...
//The HTML UI based call to addItem works and the UI updates to reflect the new data.
<table>
<tr>
<td>Status: </td>
<td><input type="text" ng-model="item.status" /></td>
</tr>
<tr>
<td>Priority Summary: </td>
<td><input type="text" ng-model="item.priority_summary" /></td>
</tr>
<tr>
<td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" /> </td>
</tr>
</table>
<div ng-repeat="item in items">
<p class="priority">{{item.priority_summary}}</p>
<p class="type">{{item.type}}</p>
</div>
Here is the JavaScript
var app = angular.module('DemoApp', []);
<!-- Define controller -->
var contrl = app.controller("MainController", function ($scope) {
$scope.items = [{
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}];
$scope.addItem = function(item)
{
alert("addItem called");
$scope.items.push(item);
$scope.item = {};
}
$scope.subscribe = function(){
//Code for connecting to the endpoint...
alert("event received"); //We receive this alert, so event is received correctly.
//***This code (items.push(item) is not working
//and we do not get the UI updated to show the newly added item.
/*Commented - NON WORKING
item.status = "New";
item.priority_summary = "High";
$scope.items.push(item);
// $scope.$apply();
});*/
//Working Code....
$scope.items.push({
status: 'New',
priority_summary: 'H'
});
$scope.$apply();
}
//calling subscribe on controller initialization...
$scope.subscribe();
However, I am having trouble understanding how can I add a new item programmatically and see those changes on the UI.
Essentially, the subscribe() function in the code snippet is listening for external events and needs to insert an item in the list programmatically / update the UI, is not working.
I have been looking for a while and tried various examples from SO and elsewhere, but I cannot get it to work for this rather simple looking case. Any pointers or guidance will be appreciated.
Thanks!

Updated in the question above...but figured I should mention the erring code and the working version here....
//NON WORKING CODE
//***This code (items.push(item) is not working
//and we do not get the UI updated to show the newly added item.
item.status = "New";
item.priority_summary = "High";
$scope.items.push(item);
// $scope.$apply();
});
//Working Code....
$scope.items.push({
status: 'New',
priority_summary: 'H'
});
$scope.$apply();

No need to pass newItem from HTML to controller since newItem is a $scope variable and controller already has access to it.
Here is the working code:
<!-- html -->
<div ng-controller="MyController">
<table>
<tr>
<td>Status: </td>
<td><input type="text" ng-model="newItem.status" /></td>
</tr>
<tr>
<td>Priority Summary: </td>
<td><input type="text" ng-model="newItem.priority_summary" /></td>
</tr>
<tr>
<td colspan="2"><input type="Button" value="Add" ng-click="addItem()" /> </td>
</tr>
</table>
<div ng-repeat="item in items">
<p class="priority">{{item.priority_summary}}</p>
<p class="type">{{item.status}}</p>
</div>
</div>
<!-- js -->
.controller('MyController', ['$scope', function($scope) {
$scope.items = [{
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}, {
"status": "New",
"priority_summary": "High"
}];
$scope.newItem = {};
$scope.addItem = function(){
$scope.items.push($scope.newItem);
$scope.newItem = {};
}
}]);

Related

AngularJS In ng-repeat splice the last object of an array and it removes the properties of a form tag

In my AngularJS app, I use ng-repeat to display rows of records. When the user removes the last record in the array, it removes the custom properties on the form tag. This is breaking all of the custom validations set to those properties. If the user removes all other records, the properties are still intact. Any help is much appreciated. Please see the plunker for code.
http://plnkr.co/edit/8s5brh7Hj9cu0gdpNpxt?p=preview
<body ng-controller="MainCtrl as vm">
<form name="vm.cptForm" role="form" ng-submit="vm.submit()" novalidate="">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>DATE OF SERVICE</th>
<th>REMOVE</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in vm.items">
<td>{{item.id}}</td>
<td type = "text"
name="name"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.name.$valid}"
ng-model="item.name">{{item.name}}</td>
<td type = "text"
name="dos"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.dos.$valid}"
ng-model="item.dos">{{item.dos}}</td>
<td>
<button class="btn btn-xs btn-danger" type="button" ng-click="vm.remove(item)">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="row">
<span class="error" ng-show="vm.showErrors && vm.cptForm.dos.$error.termedMember">
</form>
</body>
and here's the js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var vm = this;
vm.cptForm = {};
vm.items = [];
vm.items = [
{
"id":0,
"name": "Jane",
"dos":"05/05/2017"
},
{
"id":1,
"name": "Janet",
"dos":"05/05/2017"
},
{
"id":2,
"name": "John",
"dos":"05/05/2017"
},
{
"id":3,
"name": "Johnathan",
"dos":"05/05/2017"
},
{
"id":4,
"name": "Joanne",
"dos":"05/05/2017"
}
];
vm.remove = function(item){
console.log(item);
console.log(vm.cptForm); //before splice vm.cptForm contains dos and name properties
var index = vm.items.indexOf(item);
debugger;
vm.items.splice(index,1);
// console.log(vm.items);
//console.log(vm.cptForm); //after splice vm.cptForm no longer contains dos and name properties
vm.validate(vm.items);
};
vm.validate = function(items){
angular.forEach(items,function(item){
if(item.dos < getDate()){ //compared to today for code simplicity
vm.cptForm.dos.$setValidity("termedMember", false);
}
});
};
});
Edit
I tried creating a copy of the form prior to splicing it and then assigning it back to the original form, but that did not work. How can I retain the form's properties for each item when using ng-repeat?

AngularJS select with ng-options not updating referenced object property in the parent scope

My select is populating with the contents of the model, but when I select an option, the model does not update.
I'm using ng-options, not ng-repeat and my ng-model is an object on the parent scope, not a primitive, so I think I've avoided the "child-scope" issues I've seen on similar posts. I've recreated the problem on jsfiddle:
http://jsfiddle.net/bobweil/wfdjrej5/
When the user clicks on a row in the table, a small form shows up below that row, permitting a new status value to be selected for that row for posting to the backend service.
Here's my javascript:
angular.module('myApp', [])
.controller('TaskCtrl', function HomeController($scope, $filter) {
$scope.statusMasters = [{
"Id": 1,
"DisplayOrder": 100,
"Text": "Review"
}, {
"Id": 2,
"DisplayOrder": 200,
"Text": "New"
}, {
"Id": 3,
"DisplayOrder": 300,
"Text": "Working"
}, {
"Id": 4,
"DisplayOrder": 400,
"Text": "Complete"
}]
$scope.tasks = [{
"taskId": 1000,
"Descr": "My first task",
"statusId": 1
}, {
"taskId": 2000,
"Descr": "My second task",
"statusId": 1
}, {
"taskId": 3000,
"Descr": "My third task",
"statusId": 1
}];
$scope.selectedTask = null;
$scope.newTaskStatus = {};
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
$scope.newTaskStatus = {};
$scope.newTaskStatus.taskId = thisTask.taskId;
$scope.newTaskStatus.statusId = thisTask.statusId;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
});
And here's my html:
<div ng-controller="TaskCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Task #</th>
<th>Description</th>
<th>StatusId</th>
<th>Status Text</th>
</tr>
</thead>
<tbody ng-repeat="item in tasks" ng-click="selectTask(item)" ng-switch on="isSelected(item)">
<tr>
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{newTaskStatus | json}}</pre>
</div>
<label>Select a new status for task {{newTaskStatus.taskId}}:</label>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
</table>
Part of the issue is you are trying to set a click event on the tbody, you need to set the ng-click on the row (tr).
Secondly, unless it is needed for another reason, I wouldn't duplicate the values from the "selectedTask" into a "newTaskStatus" when you are planning on changing the status and sending back that value, it can all be done with one object on the scope.
Third, you could clean up your .js a little by changing the 'ng-switch on' to do the check if it is selected. It replaces an entire function with a comparison.
I would do something like this.
<tbody ng-repeat="item in tasks" ng-switch on="selectedTask.taskId == item.taskId">
<tr ng-click="selectTask(item)">
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{selectedTask | json}}</pre>
</div>
<label>Select a new status for task {{selectedTask.taskId}}:</label>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
With the .js I would remove the unnecessary items:
$scope.selectedTask = null;
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
I forked your jsfiddle here to demonstrate what I mean. Good Luck!

Showing products with angular in the html

I'm trying to make a table that shows products with angular, but the items are not showing up in HTML. If anyone notice anything wrong with the code, please help me.
This is my app.js:
(function (){
var app = angular.module('confirmados', []);
app.controler('ListaController', function (){
this.product = gem;
});
var gem = {
id: 1,
name: 'cool',
email: 'cool#'
};
And this is my HTML:
<tr ng-controller="ListaController as lista" class="reservas">
<td> {{lista.product.id}} </td>
<td> {{lista.product.name}} </td>
<td> {{lista.product.email}} </td>
</tr>
You spelled controller wrong:
app.controler
Should be
app.controller

Manipulating array data to actually save, write, and change the data content

I am new to AngularJS and so far I'm loving it but I am having a hard time manipulating my data with it. I have an array of data with the attributes name:'', description:'', type:'', ... etc ... and I have enough data but not enough yet for me to upload it onto a server. My problem is that I want to be able to change and update my data using a form or input.
Here is my scripts/admin.js where I implement function submitTheForm() that I call with the submit button.
angular.module('myApp')
//filter to get a specific $scope.campaigns using its id
.filter('getById', function() {
return function(input, id) {
var i=0, len=input.length;
// alert(input.length);
for (; i<len; i++) {
if (+input[i].id === +id) {
return input[i];
}
}
return input[0];
};
})
.controller('AdminCtrl', ['$scope', '$filter', function($scope, $filter) {
//<--ARRAY OF DATA with multiple attributes<--
$scope.campaigns = [
{ name:'', description'', etc... etc...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
];
$scope.selectCampaign = function(object) {
$scope.selectedCampaign = object;
};
$scope.submitTheForm = function(item, event) {
if (confirm("Are you sure you want to edit?") == true) {
alert("--> Submitting form");
var dataObject = {
name : $scope.selectedCampaign.name, description: $scope.selectedCampaign.description, type: $scope.selectedCampaign.type, imgSrc: $scope.selectedCampaign.imgSrc, brand: $scope.selectedCampaign.brand, region: $scope.selectedCampaign.region, location: $scope.selectedCampaign.location, contact: $scope.selectedCampaign.contact, url: $scope.selectedCampaign.url, id: $scope.selectedCampaign.id
};
console.log(dataObject);
var campaign = $scope.selectedCampaign;
var id = campaign.id;
var found = $filter('getById')($scope.campaigns, id);
// setTimeout(function(){ $scope.$apply($scope.selectedCampaign = dataObject); });
}
};
}]);
Here is my main.html where I have my input and submit button
<div class="row modalDetail">
<div class="col-xs-12 col-sm-6 col-md-6 detailLeft text-left">
<div class="middle-allign">
<h1 class="detailName">
<input type="text" ng-model="selectedCampaign.name" name="name">
</h1>
<div class="detailDescription">
<textarea rows="5" cols="71" name="description" ng-model="selectedCampaign.description"></textarea>
</div>
<table class="detailTable table">
<tbody>
<tr>
<td class="bolder">Brand</td>
<td>
<input type="text" ng-model="selectedCampaign.brand" name="brand" >
</td>
</tr>
<tr>
<td class="bolder">Campaign Type</td>
<td>
<input type="text" ng-model="selectedCampaign.type" name="type">
</td>
</tr><tr>
<td class="bolder">Region</td>
<td>
<input type="text" ng-model="selectedCampaign.region" name="region">
</td>
</tr>
<tr>
<td class="bolder">Contact</td>
<td>
<input type="text" ng-model="selectedCampaign.contact" name="contact">
</td>
</tr>
<tr>
<td class="bolder">Location</td>
<td>
<input type="text" ng-model="selectedCampaign.location" name="location">
</td>
</tr>
<tr>
<td class="bolder">URL</td>
<td>
<input type="text" ng-model="selectedCampaign.url" name="url">
</td>
</tr>
</tbody>
</table>
<div class="detailCta">
<button class="btn detailButton" ng-click="submitTheForm()">Submit Campaign</button>
</div>
</div>
</div>
I am trying to utilize 'ng-model' to bind the data and it all works fine but it does not actually change the array content within my main.html. When I refresh it all just goes back to how my array content is. This is because I haven't actually over-written my array content. How can I go about making a absolute change/over-write to the actual object within the array content?
I feel as though it is as simple as $scope.campaigns.push(campaign); except instead of 'push' it would be 'update' or 'over-write'
If you want to store the values in the server, you should see ngResource to create entry points to get and save (and delete) data. Alternatively, you can use the lower level service $http.
If you want to store the data in your current script (it will last until the page is refreshed in the browser - F5), you should do some changes:
.controller('AdminCtrl', ['$scope', '$filter', '$rootScope', function($scope, $filter, $rootScope) {
//<--ARRAY OF DATA with multiple attributes<--
if ($rootScope.campaigns === undefined) {
$rootScope.campaigns = [
{ name:'', description'', etc... etc...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
];
}
$scope.campaigns = $rootScope.campaigns;
//don't even think this above line is needed, since $scope inherits from $rootScope, but I've put it for clarity.
$scope.selectCampaign = function(object) {
$scope.selectedCampaign = object;
};
$scope.submitTheForm = function(item, event) {
if (confirm("Are you sure you want to edit?") == true) {
alert("--> Submitting form");
var dataObject = {
name : $scope.selectedCampaign.name, description: $scope.selectedCampaign.description, type: $scope.selectedCampaign.type, imgSrc: $scope.selectedCampaign.imgSrc, brand: $scope.selectedCampaign.brand, region: $scope.selectedCampaign.region, location: $scope.selectedCampaign.location, contact: $scope.selectedCampaign.contact, url: $scope.selectedCampaign.url, id: $scope.selectedCampaign.id
};
console.log(dataObject);
var campaign = $scope.selectedCampaign;
var id = campaign.id;
var found = $filter('getById')($scope.campaigns, id);
}
};
}]);
Notice how I bind it to $rootScope.campaigns - so if you navigate to another ng-view and then come back, the data is still here.
However, if you want your data survive the F5, you must either use the server side options I gave you. or use Local Storage.

Flexible table ng-repeat

Is the following possible and if so how do I change my HTML to allow it?
I have the following model;
prospect = [{"name":"jamie",
"phones": [{
"type":"home",
"number":"01275"},
{
"type":"mobile",
"number":"0788"}]},
{"name":"peter",
"phones": [{
"type":"mobile",
"number":"07852"}]}
]
and I would like to display - in an angularjs table - like this
name home mobile
jamie 01275 0788
peter 07852
My current HTML
<table>
<tbody ng-repeat='person in prospect'>
<th>Name</th>
<th ng-repeat="phone in person.phones">{{phone.type}}</th>
<tr>
<td>
{{person.name}}
</td>
<td ng-repeat='phone in person.phones'>
{{phone.number}}
</td>
</tr>
</tbody>
</table>
produces
Name home mobile
jamie 01275 0788
Name mobile
peter 07852
http://jsfiddle.net/jaydubyasee/D7f2k/
To do this in html, without modifying your json, I'd first add an array that indicates what type of phone goes into each column:
$scope.types= ["home","mobile"];
Then use it in the header:
<th ng-repeat="type in types">{{type}}</th>
Then to print out the phone numbers we iterate over each phone within each column, using ngIf to conditionally show any phones that match that column's type:
<td ng-repeat='type in types'>
<span ng-repeat='pphone in person.phones' ng-if="pphone.type == type">
{{pphone.number}}
</span>
</td>
updated fiddle
A variation would be to replace the nested ngRepeats with a custom directive that displays the correct phone for the given column and row.
I hope you will like this solution :)
I did you this dependency
bower install angular
bower install ng-tasty
bower install bootstrap
And here the full solution
<div tasty-table bind-resource="resource">
<table class="table table-striped table-condensed">
<thead tasty-thead></thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-bind="row.name"></td>
<td ng-bind="row.phones | filterTypeColumn:'home'"></td>
<td ng-bind="row.phones | filterTypeColumn:'mobile'"></td>
</tr>
</tbody>
</table>
</div>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/ng-tasty/ng-tasty-tpls.min.js"></script>
<script>
angular.module('stackOverflowAnswer', ['ngTasty'])
.filter('filterTypeColumn', function() {
return function (input, typeColumn) {
var phoneNumber;
input.forEach(function (phone) {
if (phone.type === typeColumn) {
phoneNumber = phone.number;
}
})
return phoneNumber;
};
})
.controller('StackOverflowController', function ($scope) {
$scope.resource = {
"header": [
{ "name": "Name" },
{ "home": "Home" },
{ "mobile": "Mobile" }
],
"rows": [
{
"name":"jamie",
"phones": [
{ "type":"home","number":"01275" },
{ "type":"mobile", "number":"0788"}
]
},
{
"name":"peter",
"phones": [
{ "type":"mobile","number":"07852"}
]
}
]
};
});
</script>
</body>
</html>
If you want know more about ngTasty you can find all the doc here http://zizzamia.com/ng-tasty/directive/table .
For your specific case, the solution was make a custom filter.
Ciao

Categories

Resources