scope variable not updating with ng-change - angularjs - angularjs

Seems like a simple problem though but finding it hard to fix.
There is a pagination component, that has a button & a dropdown. User can go to a page by either clicking the button or selecting that page number in dropdown.
The problem is, when I select a value in the dropdown, nothing happens. Because the scope variable doesnt change from the previous one.
aspx:
<div data-ng-app="app" data-ng-controller="ReportsCtrl">
<div id="paging-top">
<div>
<ul>
<li>
<select data-ng-model="SelectedPage" data-ng-change="ShowSelectedPage();"
data-ng-options="num for num in PageNumbers track by num">
</select>
</li>
<li data-ng-click="ShowNextPage();">Next</li>
</ul>
</div>
</div>
app.js
var app = angular.module("app", ["ngRoute"]);
ReportsCtrl.js
app.controller("ReportsCtrl", ["$scope","ReportsFactory",function ($scope,ReportsFactory) {
init();
var init = function () {
$scope.ShowReport(1);
}
$scope.ShowReport = function (pageNumber) {
GetUserResponsesReport(pageNumber);
}
function GetUserResponsesReport(pageNumber) {
$scope.UserResponsesReport = [];
var promise = ReportsFactory.GetReport();
promise.then(function (success) {
if (success.data != null && success.data != '') {
$scope.UserResponsesReport = success.data;
BindPageNumbers(50, pageNumber);
}
});
}
function BindPageNumbers(totalRows, selectedPage) {
$scope.PageNumbers = [];
for (var i = 1; i <= 5 ; i++) {
$scope.PageNumbers.push(i);
}
$scope.SelectedPage = selectedPage;
}
$scope.ShowSelectedPage = function () {
alert($scope.SelectedPage);
$scope.ShowReport($scope.SelectedPage);
}
$scope.ShowNextPage = function () {
$scope.SelectedPage = $scope.SelectedPage + 1;
$scope.ShowReport($scope.SelectedPage);
}
}]);
Say, the selected value in dropdown is 1. When I select 2 in the dropdown, the alert shows1. When Next is clicked, the dropdown selection changes to 2 as expected. Now, when I select 1 in the dropdown, the alert shows 2.
Tried to make a fiddle, but do not know how to do with a promise - http://jsfiddle.net/bpq5wxex/2/

With your OP SelectedPage is just primitive variable.
With every angular directive new scope is get created.
So,SelectedPage is not update outside the ng-repeat scope after drop-down is changed i.e. in parent scope which is your controller.
In order to do this,use Object variable instead of primitive data types as it update the value by reference having same memory location.
Try to define SelectedPage object in controller in this way.
$scope.objSelectedPage = {SelectedPage:''};
in HTML
<select data-ng-model="objSelectedPage.SelectedPage" data-ng-change="ShowSelectedPage();"
In ShowSelectedPage
$scope.ShowSelectedPage = function () {
console.log($scope.objSelectedPage.SelectedPage);
$scope.ShowReport($scope.objSelectedPage.SelectedPage);
}

Related

AngularJS: Why custom filter triggered on ng-Click?

I have a a custom filter to filter list of item name based on user input
.filter('searchFor', function () {
return function (arr, searchText) {
var result = [];
debugger
if (!arr || !arr.length) {
return;
}
if (!searchText) {
return arr;
}
......
return result;
}
})
I use in my View like this:
<ion-item ng-repeat="item in items | searchFor:searchText">
<div ng-click="DisplayItem(item.id)">
....
The problem is, when I try to trigger DisplayItem function or even tapping on the back button (both using ng-Click), the searchFor filter got triggered and the ng-Click function does not work.
If I replace the searchFor with `filter like this:
<ion-item ng-repeat="item in items | filter:searchText">
Then the ng-Click works but I can't use my custom filter.
Why is this happening?
The filter function should be a pure function, which means that it should always return the same result given the same input arguments and should not affect the external state.
Try following it will get a new copy of array input with filters applied.
.filter('searchFor', function () {
return function (arr, searchText) {
var result = [];
if (!arr || !arr.length) {
return;
}
if (!searchText) {
return [];
}
angular.forEach(arr, function(item) {
//your filtering logic should come here
result.push(item);
});
return result;
}
see more details on Custom filters

Reorder a list without ng-repeat

For reasons I won't go into, I'm not using ng-repeat for a list of items.
I have a list like this:
<p>Search: <input type="text" ng-model="search"></p>
<div id="grid" filter-list="search">
<div id="item1" class="[list of properties]">
//item content
</div>
<div id="item2" class="[list of properties]">
//item content
</div>
<div id="item3" class="[list of properties]">
//item content
</div>
<div id="item4" class="[list of properties]">
//item content
</div>
</div>
As you can see I already have a search function working well.
My app script looks like this:
<script>
var app = angular.module('myApp', []).controller('MainCtrl', function ($scope, $timeout) {
});
app.directive('filterList', function ($timeout) {
return {
link: function (scope, element, attrs) {
var div = Array.prototype.slice.call(element[0].children);
function filterBy(value) {
div.forEach(function (el) {
el.className = el.textContent.toLowerCase().indexOf(value.toLowerCase()) !== -1 ? '' : 'ng-hide';
});
}
scope.$watch(attrs.filterList, function (newVal, oldVal) {
if (newVal !== oldVal) {
filterBy(newVal);
}
});
}
};
});
</script>
The problem is I need to be able to reorder the list based on class values or even the ids (At this stage it doesn't matter).
Every tutorial/guide online assumes that the code uses "ng-repeat" ... which I simply can't use here.
Is there any way I can get the items to reorder without using ng-repeat?
Instead of using ng-repeat just sort the data.
$scope.items = $scope.items.sort(yourSortFn);
You can modify this sorting script:
<ul id="id01">
<li>Oslo</li>
<li>Stockholm</li>
<li>Helsinki</li>
<li>Berlin</li>
<li>Rome</li>
<li>Madrid</li>
</ul>
<script>
function sortList() {
var list, i, switching, b, shouldSwitch;
list = document.getElementById("id01");
switching = true;
/* Make a loop that will continue until
no switching has been done: */
while (switching) {
// Start by saying: no switching is done:
switching = false;
b = list.getElementsByTagName("LI");
// Loop through all list items:
for (i = 0; i < (b.length - 1); i++) {
// Start by saying there should be no switching:
shouldSwitch = false;
/* Check if the next item should
switch place with the current item: */
if (b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase()) {
/* If next item is alphabetically lower than current item,
mark as a switch and break the loop: */
shouldSwitch= true;
break;
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark the switch as done: */
b[i].parentNode.insertBefore(b[i + 1], b[i]);
switching = true;
}
}
}
</script>
Just target the div elements instead of li elements, and compare them based on their .className property (after formating them propelly depending on what data you get) instead of their .innerHTML
Sort or filter or reduce your array of elements any way you need then append the results
Very basic example using descending id:
scope.sortDesc = function() {
div.sort(function(a, b) {
return /\d+/.exec(b.id) - /\d+/.exec(a.id);
});
element.append(div);
}
scope.sortDesc();
Plunker demo

Multiple dropdown selection in ag-grid (link Attached)

I need to have a column in ag-grid where i can select multiple values from dropdown. I just googled online to see if it is already implemented but i could find only one link.
https://gist.github.com/gaborsomogyi/00f46f3c0ee989b73c92
Can someone let me know how to implement it. show the full code as an example please.
Here is the code shared over there.
function agDropDownEditor(params, optionsName, optionsList) {
_.set(params.$scope, optionsName+'.optionsList', optionsList);
var html = '<span style="width:100%; display:inline-block" ng-show="!'+optionsName+'.editing" ng-click="'+optionsName+'.startEditing()">{{data.'+params.colDef.field+'}}</span> ' +
'<select style="width:100%" ng-blur="'+optionsName+'.editing=false" ng-change="'+optionsName+'.editing=false" ng-show="'+optionsName+'.editing" ng-options="item for item in '+optionsName+'.optionsList" ng-model="data.'+params.colDef.field+'">';
// we could return the html as a string, however we want to add a 'onfocus' listener, which is not possible in AngularJS
var domElement = document.createElement("span");
domElement.innerHTML = html;
_.set(params.$scope, optionsName+'.startEditing', function() {
_.set(params.$scope, optionsName+'.editing', true); // set to true, to show dropdown
// put this into $timeout, so it happens AFTER the digest cycle,
// otherwise the item we are trying to focus is not visible
$timeout(function () {
var select = domElement.querySelector('select');
select.focus();
}, 0);
});
return domElement;
}
Hope this helps, this is just a snippet of my code what i'm doing is I'm fetching from an array using map and then creating my object which is col and returning it and this will repeat till the last index of that array.
var col = {};
col.field = "fieldName";
col.headerName = "colName";
col.headerCellTemplate = function() {
var eCell = document.createElement('span');
eCell.field = obj.expr;
eCell.headerName = obj.colName;
eCell.innerHTML = "<select>"+"<option>"+
'Abc'+"</option>" +"<option>"+
'Xyz'+"</option>" +"</select>"
//$scope.dropDownTemplate;
var eselect = eCell.querySelector('select');
eselect.focus();
return eCell;
};
return col ;
}));

AngularJS dirPagination Select All Visible Rows

I'm building a CRUD data management project using Angular and the dirPagination directive and need to have a "select all" checkbox that selects all rows that are visible /without/ using jQuery.
I started with this - note - I am climbing the Angular learning curve and am aware that some/all of this may not be "the Angular way" but I also don't want to start fiddling (no pun) with the guts of dirPagination, ergo the dirPagination directive:
<tr dir-paginate-start="item in items|filter:filterFunction()|orderBy:sortPredicate:reverse| itemsPerPage: rowsPerPage">
and the individual row-checkbox
<input type="checkbox" class="rowSelector" ng-model="item.isSelected" ng-change="rowSelect($index, $event)"/> status: {{item.isSelected}}
and the related model elements:
$scope.items = [] //subsequently filled
$scope.rowsPerPage = 5;
$scope.rowSelect = function (ix, $event) {
var checked = (typeof $event == 'undefined') ? false : true;
if (!checked) { $scope.masterCheck = false; }
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
$scope.items[start + ix].isSelected = checked;
};
that works as expected. Check/uncheck a row and the {{item.isSelected}} value is updated in the model and is displayed beside the checkbox.
Then I added this /outside/ of the dirPagination repeat block:
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
and the related function in the model:
$scope.masterCheck = false;
$scope.checkAll = function () {
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
var checked = $scope.masterCheck == true;
var rows = document.getElementsByClassName("rowSelector");
for (var ix = 0; ix < rows.length; ix++) {
rows[ix].checked = checked;
$scope.items[start + ix].isSelected = checked;
}
}
however in the checkAll() function checking/unchecking the individual rows isn't reflected in the {{item.isSelected}} display of each row.
Explicitly setting the individual item with
$scope.items[start + ix].isSelected = checked;
seems to set the 'isSelected' property of that item within the scope of the checkAll function but the row display does not change.
Clearly I have something wrong perhaps misunderstanding a Scope issue but at this point I'm stumped.
Any help greatly appreciated :-)
The light dawned, finally.
checkAll() as written tried to access each row by calculating its position using dir-paginate's __default__currentPage and Angular's row $index.
Of course that doesn't work because the items[] collection held by dir-paginate has been subjected to filtering and sorting, so while items[] do get checked (item.isSelected = true) the selected items/rows were living on non-visible pages. i.e. - we were selecting the wrong indexes.
One solution is comprised of the following -
The master checkbox
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
The row checkbox (note function calls)
<input type="checkbox" class="rowSelector" value="{{sourceIndex('senId',item.senId)}}" ng-model="item.isSelected" ng-click="rowSelect(this)" />
the dir-paginate directive controls tag
<dir-pagination-controls on-page-change="onPageChange(newPageNumber)" max-size="15" direction-links="true" boundary-links="true" pagination-id="" template-url=""></dir-pagination-controls>
and the related $scope values and functions:
$scope.items = [];
$scope.masterCheck = false;
$scope.onPageChange = function (newPageNumber) {
//clear all selections on page change
//dir-paginate provides this hook
$scope.masterCheck = false;
for (var i in $scope.items) {
$scope.items[i].isSelected = false;
}
}
$scope.rowSelect = function (item) {
//if one is unchecked have to turn master off
if (!item.isSelected) $scope.masterCheck = false;
}
$scope.sourceIndex = function (keyName, key) {
//gets the actual items index for the row key
//see here http://stackoverflow.com/questions/21631127/find-the-array-index-of-an-object-with-a-specific-key-value-in-underscore
//for the 'getIndexBy' prototype extension
var ix = $scope.items.getIndexBy(keyName, key);
return ix;
}
$scope.checkAll = function () {
//only visible rows
var boxes = document.getElementsByClassName("rowSelector");
for (var bix in boxes) {
var ix = boxes[bix].value;
$scope.items[ix].isSelected = $scope.masterCheck;
}
}
There is probably a better way but while not highly efficient this one works well enough for common folk.
The $scope.sourceIndex() function stuffs the actual source row index into the row checkbox as its value= attribute value.
The checkAll() function then grabs all visible rows by the "rowSelector" class and then iterates through those grabbing the data index from the checkbox value and setting the appropriate item.isSelected.
The $scope.onPageChange is specified in the dir-Paginate controls directive and ensures that when the page changes, all row selections are cleared.
Happy happy.
Your variable masterCheck is being set to false in rowSelect() and subsequently in checkAll() you are setting checked to masterCheck which then is used to assign isSelected
This line is wrong:
var checked = $scope.masterCheck == true;
Because you want to flip masterCheck so it should be:
$scope.masterCheck = !$scope.masterCheck;
and then
.isSelected = $scope.masterCheck;
You weren't ever setting $scope.masterCheck to true so it was always false and since your isSelected values depended on it, they were always false. Also, this functions as a checkAll/unCheckAll to make it only check all change to the following:
$scope.masterCheck = !$scope.masterCheck;
var checked = $scope.masterCheck == true;

Issue with view updation in AngularJS directive

I am using the following directive for 'add tag' functionality in my application:
directives.addTag = function ($http) {
return {
link: function (scope, element, attrs) {
element.bind('keypress', function (event) {
if (event.keyCode == 13) { /*If enter key pressed*/
if (!scope.$parent.post) { //For KShare
var newTagId = "tagToNote";
}
else { //For KB
var newTagId = "tagToAddFor" + scope.post.meta.id;
}
var tagValue = element[0].value;
if (tagValue == "")
return;
if (!scope.$parent.post) {
scope.$parent.tags.push(tagValue);
scope.addTagButtonClicked = false;
}
else {
scope.post.tags.push(tagValue);
scope.addTagButtonClicked = false;
}
scope.$apply();
element[0].value = "";
}
});
}
}
}
This is the HTML code for rendering the tags:
<div class="tagAdditionSpan" ng-repeat="tag in post.tags" ng-mouseenter="hover = true" ng-mouseleave="hover = false">
<span>{{tag}}</span>
<span class="deleteIconSpan" ng-class="{deleteTagIcon: hover}" ng-click="$parent.deleteTag($index,$parent.$index);"></span>
</div>
I have a textbox to add tags when a user types the name of the tag in it and presses 'Enter' key. On page load, I am statically populating 1 tag into the 'tags' array.
I am even able to add tags using the tags and it is reflected in the view. However after adding 2 or 3 tags, it starts misbehaving and the view is no longer updated with the added tags.
I tried debugging this and found that it is being updated in the 'scope.post.tags' array but is not reflected in the view.
What am I doing wrong?
Based on the comments received, I was able to solve the issue. 'ng-repeat' used to break the loop on addition of duplicate tags and hence the view was not updated accordingly.
This fixed the issue(added 'track by' in ng-repeat):
<div class="tagAdditionSpan" ng-repeat="tag in post.tags track by $index" ng-mouseenter="hover = true" ng-mouseleave="hover = false">
<span>{{tag}}</span>
<span class="deleteIconSpan" ng-class="{deleteTagIcon: hover}" ng-click="$parent.deleteTag($index,$parent.$index);"></span>
</div>

Resources