How to update a specific value inside ng-repeat in Angular? - javascript

So I have a code that gets all the products in my api
:
Controller
loadProducts();
function loadProducts() {
$http.get('/api/products').then(function(response) {
$scope.products = response.data
});
}
$scope.Update = function(id) {
//do update code here and when it succeeds, load products function
loadProducts();
}
HTML
<table>
<tr>
<td>ID</td>
<td>Name</td>
<td>Action</td>
</tr>
<tr ng-repeat="p in products">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><a ng-click="Update(p.id)"></a></td>
</tr>
</table>
The code is working though I have lots of data being loaded that whenever I update a single item, the whole data will be loaded again.
Is there a way that for example, I updated a single item,and it will be the only one being loaded instead of loadProducts(); loading everything?
Any help would be much appreciated.

On recalling of loadProducts from update your fetching same data....so you dont need to call it again.....only if u want to fetch some new based on some id or something then only you need it......have a look at below code how to replace the existing array data with index value
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.products = [
{"id":1,"name":"a"},
{"id":2,"name":"b"},
{"id":3,"name":"c"},
{"id":4,"name":"d"},
];
$scope.Update = function(id,index) {
$scope.products[index].name="h";
}
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.12/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<table>
<tr>
<td>ID</td>
<td>Name</td>
<td>Action</td>
</tr>
<tr ng-repeat="p in products">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><button ng-click="Update(p.id,$index)">upit</button></td>
</tr>
</table>
</body>
</html>
Here i added a sample data for you and when you click on a upit i am passing the index value to update function....by using this i am overriding the name property...like if you have new data from api then also you use this by fetching the data first and inserting like this.

No need to call loadProducts again, simply update the existing products array
$scope.Update = function(id, $index) {
//do update code here and when it succeeds, update the existing products array
$scope.products[$index] = updatedProduct;
}

Do not call loadProducts() on updating each item. Since angular supports Two-way data binding ,whenever you update data it will remain on the client side.
$scope.Update = function(id) {
//remove from here
loadProducts();
}
Once everything is updated, provide a way to store it on the server side. Load all the products, whenever an application is loaded initially.
In order to limit the data that needs to be shown on the interface, you can use limitTO inside ng-repeat
<tr ng-repeat="p in products | limitTo : 100">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><a ng-click="Update(p.id)"></a></td>
</tr>

Pass the property and index to update. $index you can get the current item index. Also if you need to update the second item only in products then you can check it $index==2.
<tr ng-repeat="p in products">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><a ng-click="Update(p.id, $index)"></a></td>
</tr>
</table>
$scope.Update = function(id, index) {
$scope.products[index] = updatedProduct;
}

Option 1:
Use $index to track the index of the ng-repeat.
<tr ng-repeat="p in products">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><a ng-click="Update($index)"></a></td>
</tr>
Inside your controller:
$scope.Update = function(index) {
$scope.products[index].name = newValue; //Update Name of a particular Product
}
This edition will be populated to the View.
Option 2:
Pass the product as a parameter to the controller function Update .
<tr ng-repeat="p in products">
<td>{{ p.id }}</td>
<td>{{ p.name }} </td>
<td><a ng-click="Update(p)"></a></td>
</tr>
Inside your controller:
$scope.Update = function(product) {
product.name = newName; //Update Name of a particular Product
}

Related

How to print Vue objects row by row

I have a Vue application and I am trying to display each 'object' in its own table row. However, I can only get it to display each object in one column or I can get it to a point where each element is in its own row (image below). How would I make it so '0 BTC AUD 14,745.3' is in the first row and then the next object '1 ETH AUD 312.14' is displayed in the second row. I am new to Vue and was wondering if anyone was able to help me out
I have attached an image below as well as my current code, thank you!
<template>
<div class="main">
<div id="container" v-for="(index) in coin" :key="index">
<table class="coins">
<thead>
<tr class="header">
<th>Rank</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<td>{{ index }}</td>
</tbody>
</table>
</div>
</div>
</template>
<script>
import axios from 'axios'
var limit = 20
export default {
name: 'ex',
data: () => ({
coin: []
}),
created () {
axios.get('https://min-api.cryptocompare.com/data/top/totalvolfull?limit=' + limit + '&tsym=AUD')
.then(response => {
for (var i = 0; i < limit; i++) {
this.coin.push(i, response.data.Data[i].CoinInfo.Name, response.data.Data[i].DISPLAY.AUD.PRICE)
// console.log(this.coin[i])
}
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
Change the way you are pushing data into your coin array, because here in every iteration you are pushing three items (an index, a coin name and a value) into the array but what you want to do is push a single item (an array or object) containing all this information. For code clarity also change the name of the coin array into coins. Something like this should work:
this.coins.push([i, response.data.Data[i].CoinInfo.Name, response.data.Data[i].DISPLAY.AUD.PRICE])
Then change the iteration in your template. First thing change the v-for to something like this:
<div id="container" v-for="(coin, index) in coins" :key="index">
and then when you print the content:
<tbody>
<td>{{ coin[0] }}</td>
<td>{{ coin[1] }}</td>
<td>{{ coin[2] }}</td>
</tbody>
I didn't test this, but I hope the general idea is enough to get you on the right direction.
maybe change how did you create this object
instead do that:
this.coin.push(i, response.data.Data[i].CoinInfo.Name, response.data.Data[i].DISPLAY.AUD.PRICE)
Do something like that
const coinObject = {
index: i,
name:response.data.Data[i].CoinInfo.Name,
price: response.data.Data[i].DISPLAY.AUD.PRICE
}
this.coin.push(coinObject);
and then you can loop like that in your template:
<div id="container" v-for="(coinItem, index) in coin" :key="index">
<table class="coins">
<thead>
<tr class="header">
<th>Rank</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<td>{{ coinItem.index }}</td>
<td>{{ coinItem.name }}</td>
<td>{{ coinItem.price }}</td>
</tbody>
</table>
</div>

How to remove old data and bind new data to a table while using ng-repeat?

I have a custom directive like this:
<table id="usertbl" class="table table-hover table-bordered" style="
position: relative;
top: 27px;
">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>gender</th>
<th>state</th>
<th>zip</th>
<th>email</th>
<th>mobile</th>
<th>Delete</th>
</tr>
<tr ng-repeat="x in users">
<td>{{ x.fName }}</td>
<td>{{ x.lName }}</td>
<td>{{ x.gender }}</td>
<td>{{ x.state }}</td>
<td>{{ x.zip }}</td>
<td>{{ x.email }}</td>
<td>{{ x.mobile }}</td>
<td><button class="btn btn-danger" ng-click="clickFunc(x.email)">Delete Customer</button></td>
</tr>
</table>
</body>
http.get("getallusers.do",{ params:{pageno:count,pageSize:2}}).then(function(response) {
//First function handles success
scope.users = response.data;)}
One next button is there each time clicking it will fetch 10 records from the db, but each time the 10 record is added to the existing 10 record in the ui table. i want only new 10 record to be added in the table .
Clear the array and then push the new data as below
scope.users =[];
scope.users = response.data;
You need to call $scope.$apply() after $scope.users = response.data;
$scope.users = response.data;
$scope.$apply()
Working demo
You have to methods to do this
First, you can clear the array and then push the new data
scope.users = [];
scope.users = response.data;
Second, you can splice and insert new data
//This will recreate the array as well
scope.users.splice(0, scope.users.length, ...response.data);
So it will be like this
http.get("getallusers.do",{ params:{pageno:count,pageSize:2}}).then(function(response) {
//First function handles success
scope.users = []
scope.users = response.data;
//OR
scope.users.splice(0, scope.users.length, ...response.data);
)}
UDPATE
What I am assuming from your comments is you are trying to remove values which are same in response.data and users.
You could do something like this.
scope.users = scope.users.filter(function(obj) {
return response.data.indexOf(obj) == -1; });

Get place in table of icon using ng-click

I have a table that's generated using ng-repeat with some columns headers. On the column headers, I want to columns to be sorted. I included a font-awesome sort icon next to their text and when it's clicked, I call an ng-click with ng-click="sortColumn()". I'm calling this for every column header.
How do I know which column header got clicked on? Right now it fires for each header column.
How do pass the current column in using ng-click?
I tried to pass in this, but it returns the $scope object.
Here's what I have so far:
HTML:
<table class="table">
<thead>
<tr>
<td><b>Product </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><b>Code </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><B>Available </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><B>Price </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
</tr>
</thead>
<tbody>
{{ vm.noProducts }}
<tr ng-repeat="product in vm.products">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
</tr>
</tbody>
</table>
Javascript:
vm.sortColumn = function (obj) {
console.log(obj);
}
I don't think that you can just pass column as object to sortColumn(). If you need column in that function you can loop trough products and make a new array that will be populated for example with all product names.
Maybe that function could look like this:
function getColumn(val){ // 'productName'
var column = [];
for(var i in $scope.products){
column.push(column.push($scope.products[i][val]))
}
return column
}
If you want to sort table you can use orderBy filter.
To your sortColumn() function pass parameter name and in your ng-repeat use orderBy to order column ascending/descending by one of the parameters.
<table class="table">
<thead>
<tr>
<td ng-click="sortColumn('productName')"><b>Product </b></td>
<td ng-click="sortColumn('productCode')"><b >Code </b></td>
<td ng-click="sortColumn('releaseDate')"><b >Available </b></td>
<td ng-click="sortColumn('price')"><b >Price </b></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | orderBy: criteria">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
</tr>
</tbody>
</table>
In your controller set default value for orderBy criteria. (I used here productName)
$scope.criteria = 'productName';
$scope.sortColumn = function(val){
console.log(val)
if($scope.criteria == val){
$scope.criteria = "-"+val;
} else {
$scope.criteria = val;
}
If user clicks on productCode for example, all rows will be sorted by that criteria ascending ('productCode'), if user click on productCode once again all will be sorted descending by criteria '-productCode'
I created small plunker to demonstrate that. Hope it helps.

filter is really slow on ng-repeat in angularjs

I am displaying data from a web api in a table using an ng-repeat. I want to be able to filter that data using several different textboxes and select lists that align with the table columns (8 filters total). This table usually contains 10000+ rows. Here is how I am currently doing the filtering:
HTML:
<div ng-app="myApp" ng-controller="myCtrl">
<div class="col-md-12">
<h1>Multiple Filter Example</h1>
<hr/>
<input type="text" placeholder="ID Filter" class="form-control" ng-model="IdFilter" />
<input type="text" placeholder="Title Filter" class="form-control" ng-model="TitleFilter" />
<input type="text" placeholder="URL Filter" class="form-control" ng-model="UrlFilter" />
<br />
<table class="table">
<thead>
<tr>
<th>Album ID</th>
<th>ID</th>
<th>Title</th>
<th>URL</th>
<th>Thumbnail URL</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in data | filter:{id:IdFilter} | filter:{title: TitleFilter} | filter:{url: UrlFilter} ">
<td>{{ item.albumId }}</td>
<td>{{ item.id }}</td>
<td>{{ item.title}}</td>
<td>{{ item.url }}</td>
<td>{{ item.thumbnailUrl}}</td>
</tr>
</tbody>
</table>
</div>
</div>
Here is my JS:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
var vm = this;
vm.IdFilter = "";
vm.TitleFilter = "";
vm.UrlFilter = "";
$http.get("https://jsonplaceholder.typicode.com/photos")
.then(function(response) {
$scope.data = response.data;
console.log($scope.data);
});
});
plnkr example
As you can see the filter works but it is really really slow. Is there a better way to do this and speed it up? When typing into the filter text box there is a lag after i push a key on my keyboard.
Each filter function iterates through your data once. So if you have 8 filters and 10,000+ rows, you're looking at 80,000+ rows iterated.
I'd recommend writing a custom filter that loops through your 10,000+ rows once and applies all 8 filters at once.
app.filter('uberFilter', function() {
return function(data, filterCriteria) {
var filtered = [];
angular.forEach(data, function(item) {
if( passFilter(item, filterCriteria) {
filtered.push(item);
}
});
return filtered;
}
})
Your filterCriteria should contain all the filter criteria
filterCriteria = {
id: ...
, title: ...
, url: ...
}
passFilter() should check the item against all the filterCriteria's criteria
Your html should pass filterCriteria into the custom uberFilter
<tr ng-repeat="item in data | uberFilter:filterCriteria">
This should hopefully speed things up by a factor of 8.

Using ng-switch to populate a table

I'm using a Angular.js on the front end to populate a table. I want to use ng-switch to display only data that has specific data in one column, for example only show ‘week 1’ data from a list of a NFL schedule, where one column in the data is Weeks.
So right now this code doesn't show anything in the table. If anyone could help explain this it would be greatly appreciated. Maybe I should be using ng-if ? Maybe I should have a button to press to show week 1, week 2 etc.. What's the best solution for this type of situation?
Here's the controller..
// #########################
// Predictions Controller
// #########################
BLV_app.controller('PredictionsController', function($scope, PredictionsFactory, $routeParams) {
PredictionsFactory.getPredictions(function(data) {
$scope.predictions = data;
});
});
Here's the factory..
// ---------------------------
// Prediction Factory
// ---------------------------
BLV_app.factory('PredictionsFactory', function($http) {
var factory = {};
var predictions = [];
factory.getPredictions = function(callback) {
$http.get('/predictions').success(function(output) {
predictions = output;
console.log("prediction factory", predictions);
callback(output);
});
};
return factory;
});
Here's the html..
<table class="table-striped" id="table-style">
<thead id="table-header">
<tr>
<th class="text-center">HomeTeam</th>
<th class="text-center">AwayTeam</th>
<th class="text-center">Prediction</th>
<th class="text-center">Result</th>
</tr>
</thead>
<tbody ng-repeat="predict in predictions" >
<div ng-model="predict.Week"></div>
<tr ng-switch="predict.Week">
<div ng-switch-when="1">
<td ng-if="$odd" style="background-color:#f1f1f1">{{ predict.HomeTeam }}</td>
<td ng-if="$even">{{ predict.HomeTeam }}</td>
<td ng-if="$odd" style="background-color:#f1f1f1">{{ predict.AwayTeam }}</td>
<td ng-if="$even">{{ predict.AwayTeam }}</td>
<td ng-if="$odd" style="background-color:#f1f1f1">{{ predict.Prediction }}</td>
<td ng-if="$even">{{ predict.Prediction }}</td>
<td ng-if="$odd" style="background-color:#f1f1f1">{{ predict.Result }}</td>
<td ng-if="$even">{{ predict.Result }}</td>
</div>
</tr>
</tbody>
</table>
I believe there is something wrong with ng-repeat and ng-switch when binding to elements like tbody and td. Replace them with div and li makes it working properly: JSFiddle.
<div ng-repeat="predict in predictions">
<div ng-model="predict.id"></div>
<div ng-switch="predict.id">
<div ng-switch-when="1">
<li ng-if="$odd" style="background-color:#f1f1f1">{{ predict.name }}</li>
......
Changing from <div ng-repeat="predict in predictions"> to <tbody ng-repeat="predict in predictions"> makes it not working: JSFiddle.
In ng-repeat docs and ng-switch docs, it says:
Usage
as attribute:
<ANY
ng-repeat="">
...
</ANY>
But obviously they cannot be used on ANY elements.

Categories