Populate table from JSON with search - javascript

I was able to purely implement a grid from a JSON object - AngularJS ng-repeat to populate grid from array. However, due to the nature of the added indices, being able to create a search bar with ng-model and filter:search does not work - it only can search for the first in each table row.
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
}
);
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 5 items
return !((obj._index = idx) % 5);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search.Country' />
<table>
<tr ng-repeat="work in data | filter:getFiltered | filter:search">
<td>{{work.Country}}</td>
<td>{{data[work._index+1].Country}}</td>
<td>{{data[work._index+2].Country}}</td>
<td>{{data[work._index+3].Country}}</td>
<td>{{data[work._index+4].Country}}</td>
</tr>
</table>
</body>
The length of data may or not cause the table to look like a perfect rectangle.
I'm working on making a function to split up the array and create the grid in JavaScript itself, but I'm still not sure how to filter it via search input.
Second try (with the mentioned function, but no filters at all yet...):
var test= angular.module("app", []);
function createGrid(arr, width) {
newArr = [];
reps = Math.ceil(arr.length/width) * width;
k = 0;
for (var i = 0; i < reps/width; i++) {
newArr[i] = [];
}
for (var i = 0; i < reps/width; i++) {
for (var j = 0; j < width; j++) {
(arr[k]) ? newArr[i][j] = arr[k] : newArr[i][j] = "";
//console.log(i, j, arr[k]);
k++;
}
}
return newArr;
}
test.controller("appControl", function($scope, $http) {
$scope.gridWidth = 4;
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = createGrid(Object.keys(response.records).map(function(k) { return response.records[k] }), $scope.gridWidth);
}
);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search.Country' />
<table>
<tr ng-repeat="row in data">
<td ng-repeat='work in row'>
{{ work.Country }}
</td>
</tr>
</table>
</body>

You could try something like this:
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
$scope.filteredData= response.records;
}
);
$scope.$watch('search', function () {
var array=[];
for(var i in $scope.data)
{
if($scope.search==undefined || $scope.search.length == 0 || ($scope.data[i].Country!=undefined&&$scope.data[i].Country.toUpperCase().startsWith($scope.search.toUpperCase()))){
array.push($scope.data[i]);
}
}
$scope.filteredData=array;
});
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 3 items
return !((obj._index = idx) % 5);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search' />
<table>
<tr ng-repeat="work in filteredData | filter:getFiltered | filter:search">
<td>{{work.Country}}</td>
<td ng-show="filteredData[work._index+1]">{{filteredData[work._index+1].Country}}</td>
<td ng-show="filteredData[work._index+2]">{{filteredData[work._index+2].Country}}</td>
<td ng-show="filteredData[work._index+3]">{{filteredData[work._index+3].Country}}</td>
<td ng-show="filteredData[work._index+4]">{{filteredData[work._index+4].Country}}</td>
</tr>
</table>
</body>

You could prefilter the items after a successful Ajax call and every time your model search changes.
Save the previously filtered items into $scope.workers.
Use $scope.$watch to watch for changes in the model search
Use the function searched(data) to filter for the entries that have characters given in the search field using the indexOf method. If the filter is empty, also show every item (typeof $scope.search == 'undefined').
If you want the search be case insensitive, transform searchand the Country .toLowerCase(), when using .indexOf()
Then you will only need one Angular filter $scope.getFiltered(), which makes sure, that the entries are in rows of five.
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
$scope.workers = $scope.searched($scope.data);
}
);
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 5 items
return !((obj._index = idx) % 5);
};
$scope.searched = function (data) {
var array = [];
var max = 0;
if (typeof data === 'object') {
max = data.length;
}
for (var i = 0; i < max; i += 1) {
if (typeof $scope.search == 'undefined' || data[i].Country.toLowerCase().indexOf($scope.search.toLowerCase()) != -1) {
array.push(data[i]);
}
}
return array;
};
$scope.$watch('search', function () {
$scope.workers = $scope.searched($scope.data);
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search' />
<table>
<tr ng-repeat="work in workers | filter:getFiltered">
<td>{{ work.Country }}</td>
<td>{{ workers[$index+1].Country }}</td>
<td>{{ workers[$index+2].Country }}</td>
<td>{{ workers[$index+3].Country }}</td>
<td>{{ workers[$index+4].Country }}</td>
</tr>
</table>
</body>

Related

Setting ng-model for table in AngularJS

I am working on a MEAN stack app which fetches mappings from mongodb and displays it in the table. I am using a filter to iterate over the map. Following are the code snippets:
For Controller:
app.controller('MainCtrl', function($scope) {
$scope.data = new Map();
$scope.data.set("prop1","as");
$scope.data.set("prop2","ssas");
});
For Filter:
app.filter('fromMap', function() {
return function(input) {
var out = {};
input.forEach((v, k) => out[k] = v);
return out;
}
});
and HTML:
<body ng-controller="MainCtrl">
<table>
<tr ng-repeat="(key, val) in data | fromMap"><td>{{key}}</td><td>{{val}}</td></tr>
</table>
</body>
What I want to achieve is to both key & value editable. I tried setting ng-model for both key & value in as
<td><input type="text" class="form-control" ng-model="data[key]"></td>
<td><input type="text" class="form-control" ng-model="data[val]"></td>
but this creates an empty text box in both the column. How to set the value in the text field?
angular.module('app', []).controller('ctrl', function($scope) {
$scope.data = new Map();
$scope.data.set("prop1", "as");
$scope.data.set("prop2", "ssas");
$scope.out = [];
$scope.data.forEach((v, k) => $scope.out.push({
key: k,
value: v
}))
$scope.print = function() {
var out = {};
$scope.data.forEach((v, k) => out[k] = v);
return out;
}
$scope.$watch('out', function(newVal, oldVal) {
for (var item of newVal) {
var oldItem = oldVal.filter(x => x.key == item.key)[0];
if (!oldItem) {
$scope.data.delete(oldVal[newVal.indexOf(item)].key);
$scope.data.set(item.key, item.value);
return;
} else {
if (oldItem.value != item.value) {
$scope.data.set(item.key, item.value);
return;
}
}
}
}, true)
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl'>
<ul>
<li ng-repeat='item in out'>
<input type='text' ng-model='item.key' />
<input type='text' ng-model='item.value' />
</li>
</ul>
{{print() | json}}
</div>

Custom filter for comparing dates in angularjs

I want to display only items from list that are between the two selected dates from input fields. This is my custom controller:
// Custom filter for comparing dates
snippet.filter('snippetsByDate', function(){
return function(items, fromDate, toDate){
var filtered = [];
//here you will have your desired input
var from_date = Date.parse(fromDate);
var to_date = Date.parse(toDate);
alert("From dateeeee " + from_date);
angular.forEach(items, function(item) {
if(Date.parse(item.created) >= from_date && Date.parse(item.created) <= to_date) {
filtered.push(item);
}
});
return filtered;
};
});
The alert() in the filter's function is returning undefined for the variable fromDate and NaN for the variable from_date. This is how I call my filter:
<tr ng-repeat="s in snippets | filter:description | snippetsByDate : from_date : to_date">
<td>{{s.description}}</td>
<td>{{s.language}}</td>
<td>{{s.url}}</td>
<td>{{s.user}}</td>
</tr>
And my input fields are below this code (don't know if that metters) and look like this:
<label>Filter snippets by date</label><br>
<label>From: </label>
<input type="date" ng-model="from_date"><br>
<label>To: </label>
<input type="date" ng-model="to_date">
When I delete the custom filter from ng-repeat it is working fine, but with that it looks like I am getting empty list.
I should mention that in my class I have variable created as java.util.Date class.
Thank you in advance.
Please check this working code.
var snippet = angular.module('plunker', []);
snippet.filter('snippetsByDate', function () {
return function (items, fromDate, toDate) {
var filtered = [];
//here you will have your desired input
var from_date = Date.parse(fromDate);
var to_date = Date.parse(toDate);
if (from_date == null || toDate == null)
filtered = items;
else
filtered = items.filter(function (item) {
return item.created >= from_date && item.created <= to_date;
});
return filtered;
};
});
snippet.controller('MainCtrl', function ($scope) {
$scope.snippets = [];
for (var i = 0; i < 11; i++) {
$scope.snippets.push({
description: 'description-' + i,
language: i % 2 == 0 ? 'Hindi' : 'English',
url: 'url-' + i,
user: 'User-' + i,
created: new Date().setMonth(i)
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js" ></script>
<div ng-app="plunker" ng-controller="MainCtrl">
<table>
<tr>
<td>
<label>Filter snippets by date</label><br>
<label>From: </label>
<input type="date" ng-model="from_date"><br>
<label>To: </label>
<input type="date" ng-model="to_date">
</td>
</tr>
<tr ng-repeat="s in snippets | filter:description | snippetsByDate : from_date : to_date">
<td>{{s.description}}</td>
<td>{{s.language}}</td>
<td>{{s.url}}</td>
<td>{{s.user}}</td>
</tr>
</table>
</div>

how to make a correct search when pagination

I can not understand. How to make a correct search when pagination?
English for bad writing.
I did so:
var app = angular.module('appTelDirectory', []);
app.controller('directoryList', function($scope) {
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.users = [{}]
$scope.numberOfPages = function() {
return Math.ceil($scope.users.length / $scope.pageSize);
}
for (var i = 0; i < 45; i++) {
$scope.users.push({
'name': 'user' + i
});
}
});
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="appTelDirectory" ng-controller="directoryList">
<input placeholder="Поиск..." ng-model="searchAll" class="form-control">
<ul>
<li ng-repeat="item in users | filter:searchAll | startFrom:currentPage*pageSize | limitTo:pageSize">{{item.name}}</li>
</ul>
<table>
<tr ng-repeat="item in users | startFrom:currentPage*pageSize | limitTo:pageSize">
</table>
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= users.length/pageSize - 1" ng-click="currentPage=currentPage+1">
Next
</button>
</div>
How do I change the number of items, depending on the user list. NumberOfPages unchanged...
You can use a separate list for it like this. Basically, I'm using another list filteredUsers. Now instead of using filter in the view i.e. filter:searchAll, I'm doing the same thing using the underlying $filter service in the $watch which will be invoked as I type in the field.
Now, we always have the filtered users in the filteredUsers scope variable so your further calculation now can be based on the $scope.filteredUsers not on $scope.users.
var app = angular.module('appTelDirectory', []);
app.controller('directoryList', function($scope, $filter) {
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.users = [{}];
// Using a separate list of filtered users
$scope.filteredUsers = [{}];
$scope.numberOfPages = function() {
return Math.ceil($scope.filteredUsers.length / $scope.pageSize);
}
for (var i = 0; i < 45; i++) {
$scope.users.push({
'name': 'user' + i
});
}
$scope.filteredUsers = angular.copy($scope.users);
$scope.$watch('searchAll', function(newValue) {
// Manually filtering here instead doing in the view
$scope.filteredUsers = $filter('filter')($scope.users, {$: newValue});
});
});
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="appTelDirectory" ng-controller="directoryList">
<input placeholder="Поиск..." ng-model="searchAll" class="form-control">
<ul>
<li ng-repeat="item in filteredUsers | startFrom:currentPage*pageSize | limitTo:pageSize">{{item.name}}</li>
</ul>
<table>
<tr ng-repeat="item in users | startFrom:currentPage*pageSize | limitTo:pageSize">
</table>
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= filteredUsers.length/pageSize - 1" ng-click="currentPage=currentPage+1">
Next
</button>
</div>

how to bind sum of string array values to $scope

I am trying to bind the sum of selected checkboxes from a table. I am almost there but I cannot figure out what I am doing wrong. The picture shows 2 selected boxes
you see the result of my code. I am open to suggestions if there is a better way of going about this.
$http.get('/api/Products/').success(function (data, status) { $scope.productList = data; });
$scope.selection = [];
$scope.OrderAmount = []
$scope.myTotal = 0;
$scope.toggleSelection = function toggleSelection(ProductId) {
var idx = $scope.selection.indexOf(ProductId);
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
else {
$scope.selection.push(ProductId);
}
for (var i = 0; i < $scope.selection.length; i++) {
var OrderProductId = $scope.selection[i]
var data = Enumerable.From($scope.productList).Where("x => x.ProductId == '" + OrderProductId + "'").ToArray();
$scope.OrderAmount.push(data[0].ProductPrice)
// $scope.OrderAmount = ["8500", "8500"]
for (var i = 0, len = $scope.OrderAmount.length; i < len; i++) {
$scope.myTotal += $scope.OrderAmount[i][0];
};
};
$scope.$watch('myTotal', function (value) {
$scope.model.OrderAmount = value;
});
};
view
<table class="table">
<th>Product</th>
<th>Price</th>
<tbody>
<tr ng-repeat="model in products">
<td>
<div class="toggle-switch" data-ts-color="blue">
<input id="{{model.ProductId}}" type="checkbox" hidden="hidden" ng-checked="selection.indexOf(model.ProductId) > -1" ng-click="toggleSelection(model.ProductId)">
<label for="{{model.ProductId}}" class="ts-helper"></label>
</div>
</td>
<td>{{model.ProductName}}</td>
<td>{{model.ProductPrice}}</td>
</tr>
</tbody>
</table>
<div class="form-group">
<input type="text" ng-model="model.OrderAmount" class="form-control fg-input">
</div>
UPDATE to first answer
You are doing the data binding wrongly. The checked status should be bound using ng-model but not ng-checked. You can make this easy by using an attribute (in the example checked) inside model and then loop over products to calculate the sum.
<tr ng-repeat="model in products">
<td>
<div class="toggle-switch" data-ts-color="blue">
<input id="{{model.ProductId}}" type="checkbox" hidden="hidden" ng-model="model.checked" ng-click="toggleSelection()">
<label for="{{model.ProductId}}" class="ts-helper"></label>
</div>
</td>
<td>{{model.ProductName}}</td>
<td>{{model.ProductPrice}}</td>
</tr>
Controller:
$scope.toggleSelection = function() {
var sum = 0;
angular.forEach($scope.products, function(value){
if (value.checked) sum += value.ProductPrice;
});
$scope.model.OrderAmount = sum;
}

angular result and pagination doesnt get updated on filter

I have this angular app, where I display a table based on the records, with filters, but the problem is when I eneter a value in filter the records get filter but after removing filter value, it doesnt get updated, and also pagination keeps when we click next numbers keep reducing, I am new to angularjs any help would be greatly appreciated.
This is my html code:
<input type="search" placeholder="Search By Any..." ng-model="search.$" />
table-striped table-bordered">
<thead>
<tr>
<th>User</th>
<th>Content Type</th>
<th>Content Name</th>
<th>Start Time</th>
<th>End Time</th>
<th>Duration(In Secs)</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in filteredRecords
| filter: search
| offset: currentPage*itemsPerPage
| limitTo: itemsPerPage
| orderBy:sort:reverse track by $index">
<td>{{record.user}}</td>
<td>{{record.contentType}}</td>
<td>{{record.contentName}}</td>
<td>{{record.startTime}}</td>
<td>{{record.endTime}}</td>
<td>{{record.duration}}</td>
</tr>
</tbody>
<tfoot>
<td colspan="6">
<div class="pagination pull-left">
<ul>
<li ng-class="prevPageDisabled()"><a href
ng-click="prevPage()">« Prev</a></li>
<li ng-repeat="n in range()"
ng-class="{active: n == currentPage}" ng-click="setPage(n)">
{{n+1}}
</li>
<li ng-class="nextPageDisabled()"><a href
ng-click="nextPage()">Next »</a></li>
</ul>
</div>
</td>
</tfoot>
This is angular code:
angular.module("contentViewStatusApp")
.controller("contentViewStatusController", function($scope,
$filter,contentViewStatusService)
{
var records = contentViewStatusService.list();
$scope.changeSort = function(value)
{
if ($scope.sort == value)
{
$scope.reverse = !$scope.reverse;
return;
}
$scope.sort = value;
$scope.reverse = false;
}
$scope.itemsPerPage = 8;
$scope.currentPage = 0;
$scope.filteredRecords = [];
$scope.items = [];
$scope.range = function()
{
var rangeSize = 5;
var ret = [];
var start;
start = $scope.currentPage;
if (start > $scope.pageCount() - rangeSize && $scope.pageCount() > rangeSize)
{
start = $scope.pageCount() - rangeSize + 1;
}
if($scope.pageCount() > rangeSize)
for (var i = start; i < start + rangeSize; i++)
{
ret.push(i);
}
else
for (var i = start; i < $scope.pageCount()+1; i++)
{
ret.push(i);
}
return ret;
};
var filterBy = $filter('filter');
$scope.$watch('search', function(newValue)
{
$scope.filteredRecords = filterBy(records, newValue);
}, true);
$scope.prevPage = function()
{
if ($scope.currentPage > 0)
{
$scope.currentPage--;
}
};
$scope.prevPageDisabled = function()
{
return $scope.currentPage === 0 ? "disabled" : "";
};
$scope.pageCount = function()
{
return Math.ceil($scope.filteredRecords.length / $scope.itemsPerPage) - 1;
};
$scope.nextPage = function()
{
if ($scope.currentPage < $scope.pageCount())
{
$scope.currentPage++;
}
};
$scope.nextPageDisabled = function()
{
return $scope.currentPage === $scope.pageCount() ? "disabled" : "";
};
$scope.setPage = function(n)
{
$scope.currentPage = n;
};
});
#Mckenzie, for your scenario already plugin available where you can get more out of it.
Check out this plugin: ng-table ( https://github.com/esvit/ng-table )
See this example it does all your job that you require.
ng-table combining sorting and filtering

Categories