Setting ng-model for table in AngularJS - javascript

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>

Related

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>

AngularJS ng-click not firing

I have a custom AngularJS directive for a textdown control. In it is an ng-repeat that is printing a list of div's for the emulated dropdown, and each item has an ng-click function. That function will not fire when the div is clicked. Can you please help me figure out why?
Plunkr: https://plnkr.co/edit/vOwtjqltq2WfCM9A0dFJ?p=preview
I don't remember where I first heard that concept, but it's very similar to StackOverflow's question Tags input, except you can only select 1 item. If you haven't seen that example, it is a text input that has a dropdown list when you start typing into it with related items that has fields that partially match what you've typed so far. Then the user can click on an item in the dropdown and it fills in the text input.
Here is the main page's HTML:
<!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.7/angular.js" data-semver="1.4.7"></script>
<script src="app.js"></script>
<script src="textdown.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello and welcome to the Textdown example!</p>
<label>City:
<textdown input-placeholder-text="Select a City..." is-editable="true" items="cities" ng-model="selectedCity" title="Name" width="150px"></textdown>
</label>
</body>
</html>
Here is the directive's HTML:
var HYG_TEXTBOX_DROPDOWN_TEMPLATE = '\
<div class="hyg-textdown-container activate-textdown" \
ng-class="{ \'hyg-focused\': isFocused() }"> \
<input type="search" class="activate-textdown" placeholder="{{ inputPlaceholderText }}" style="width: 100%;" \
ng-class="{ \'invalid\': !isValid() }" \
ng-change="onInputChanged()" \
ng-focus="onInputFocus($event)" \
ng-model="input" \
ng-blur="onInputBlur()" \
ng-show="isEditable"> \
</input> \
<div class="hyg-textdown-list activate-textdown" ng-show="selectActive" ng-style="{ top: ytop, left: xleft }" style="z-index:5; width: {{ width }}"> \
<div class="hyg-textdown-listed activate-textdown" \
ng-repeat="item in items | property: title: (ngModel != null ? \'\' : input) | orderBy: title | limitTo:5" \
ng-class="{ \'hyg-textdown-listed-active\': isSelected(item) }" \
ng-click="selectItem(item, $event);"> \
<span class="activate-textdown">{{ item[title] }}</span> \
</div> \
</div> \
</div>';
Here is the module, directive, controller, and associated filter code:
angular.module("hyg.Textdown", [])
.directive("textdown", ["$compile", "$document", "$filter", "$log", "$timeout", function ($compile, $document, $filter, $log, $timeout) {
return {
restrict: "E",
replace: false,
controller: "hygTextdownCtrl",
template: function (element, attrs) {
return HYG_TEXTBOX_DROPDOWN_TEMPLATE;
},
require: "?ngModel",
scope: {
inputPlaceholderText: "#",
isEditable: "=",
items: "=",
ngModel: "=",
title: "#",
width: "#"
},
link: function (scope, element, attrs) {
scope.orderBy = $filter("orderBy");
if (scope.isEditable == null)
scope.isEditable = true;
$document.bind("click", function (e) {
var shouldHideSelectList = !Enumerable.From(e.target.classList).Any(function (x) { return x == "activate-textdown"; });
if (shouldHideSelectList) {
$timeout(function () { scope.selectActive = false; }, 0);
}
});
scope.destroy = function () {
if (scope.handler != null)
scope.handler();
};
scope.isFocused = function () {
return scope.focus;
};
scope.isSelectActive = function () {
return scope.selectActive;
};
scope.isValid = function () {
return scope.input == null || scope.input.length == 0 || scope.ngModel != null;
};
scope.onInputChanged = function () {
var input = scope.input == null ? null : scope.input.toLowerCase();
var item = Enumerable.From(scope.items).Where(function (x) { return x[scope.title].toLowerCase() == input; }).ToArray()[0];
scope.selectItem(item);
};
scope.onInputFocus = function ($event) {
scope.focus = true;
scope.selectActive = true;
};
scope.onInputBlur = function () {
scope.focus = false;
scope.selectActive = false;
};
scope.selectItem = function (item, $event) {
if (scope.isEditable) {
scope.ngModel = item;
if (item != null)
scope.selectActive = false;
}
};
scope.isSelected = function (item) {
return scope.ngModel == item;
};
scope.handler = scope.$watch("ngModel", function () {
if(scope.ngModel != null)
scope.input = scope.ngModel[scope.title];
});
}
}
}])
.controller("hygTextdownCtrl", ["$scope", function ($scope) {
$scope.focus = false;
$scope.handler = null;
$scope.selectActive = false;
}])
.filter("property", ["$filter", function ($filter) {
return function (array, propertyString, target) {
if (target == null)
return array;
var matched = [];
var toMatch = target.toLowerCase();
angular.forEach(array, function (item) {
if (item[propertyString].includes != undefined) {
if (item[propertyString].toLowerCase().includes(toMatch)) {
matched.push(item);
}
}
else
{
if (item[propertyString].toLowerCase().indexOf(toMatch) > -1) {
matched.push(item);
}
}
});
return matched;
}
}]);
Thanks,
Jibba
The reason why the ng-click is not firing is because before the option is clicked, blur event is fired on the input, which hides the options and your option never gets clicked.
You can try selecting option using ng-mousedown instead of ng-click.

Why my filter doesn't work

I make filter by price, my js code bellow
(function () {
'use strict';
angular
.module('beo.products.controllers')
.controller('ProductsListController', ProductsListController);
ProductsListController.$inject = ['$scope', '$http', '$routeParams'];
activate();
function activate() {
$http.get('api/v1/products/').success(function (data) {
$scope.products = data;
$scope.filterPriceFrom = 0;
$scope.filterPriceTo = 200000;
$("#ex2").slider({
range: true,
min: 0,
max: 200000,
values: [0, 200000],
slide: function (event, ui) {
var from = $("#price-from");
from.val(ui.values[0]);
var to = $("#price-to");
to.val(ui.values[1]);
var scope = angular.element($("#html")).scope(); //где #app_dom_id айди div в котором указан ng-app.
scope.$apply(function () {
scope.byPrice(ui.values[0], ui.values[1]);
});
}
});
$scope.byPrice = function (minValue, maxValue) {
return function predicateFunc(product) {
if (product.price >= minValue && product.price <= maxValue) {
return product
} else {
return null;
}
};
};
});
)();
html in which I create a slider to filter products by price:
<div initpriceslider class="filter-content filter-price" ng-class="{opened: pctrl.spoilers.priceFilter}">
<div class="filter-wraper">
<div class="price-label">
<input type="text" id="price-from" name="price-from" ng-model="filterPriceFrom" readonly>
<input type="text" id="price-to" name="price-to" ng-model="filterPriceTo" readonly>
</div>
<div id="ex2" class="price-input"></div>
</div>
</div>
filter i use like these:
<div class="catalog-item" ng-repeat="product in products | filter: byPrice( filterPriceFrom, filterPriceTo) |orderBy:ProductSortLeft">
</div>
My problem is that the transfer of value in the filter does not work, please tell me where I made ​​mistakes
Your $scope.apply code in the slider function should be updating a $scope variable, not calling the byPrice() function.
scope.$apply(function () {
scope.minValue = ui.values[0];
scope.maxValue = ui.values[1];
});
Your filter function should return either true or false, which will either show or hide it when it's referenced by the ng-repeat directive.
$scope.byPrice = function (item) {
if (item.price >= scope.minValue && item.price <= scope.maxValue) {
return true
} else {
return false;
}
};
Or you can use ng-show and ng-hide to dynamically show or hide elements based on the same test.

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>

Populate table from JSON with search

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>

Categories