I have a simple table app which gets JSON data from a database. It passes the data via parameter to my app controller, which then filters the data. This works great. However, it is a lot of data (hundred thousand objects). I have search boxes that I use to try and filter the data, and when the search watch should be getting called (when someone types something in the search box), it doesn't. Am I missing something?
js:
var app = angular.module('SortingTables', ['ui.bootstrap']);
//Dependencies which are services, providers or factories must map to string types, which are then passed into the instance function
app.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
};
});
app.controller('Ctrl', function ($scope, filterFilter, dataTable) {
$scope.currentPage = 1;
$scope.itemsPerPage = 25;
$scope.totalItems = 0;
$scope.predicate = '';
$scope.searchBuffer = {
$: ''
};
$scope.filtered;
//This function has sort of been abstracted...
//The purpose of this function is to delay the update so the user gets a chance to finish typing before the filter is applied.
//Newer versions of angularjs have the ng-model-options: debounce=100 but we can't use that since we have IE 8 on dev boxes
$scope.$watch('searchBuffer', function (term) {
console.log('The watch on searchBuffer was called');
$scope.filtered = filterFilter(dataTable, term);
$scope.totalItems = $scope.filtered.length;
});
$scope.pageChanged = function () {
$scope.currentRow = $scope.currentPage * $scope.itemsPerPage - $scope.itemsPerPage;
};
});
html
<div ng-app="Components" ng-controller="Ctrl">
<hr/>
<table class="table table-striped">
<tr>
<th>Technical Owner
<br />
<input type="search" ng-model="searchBuffer['Technical Owner']">
</a>
</th>
<th>Branch
<br />
<input type="search" style="width: 40px" ng-model="searchBuffer.Branch">
</a>
</th>
<th>Sub Pillar
<br />
<input type="search" ng-model="searchBuffer['Sub Pillar']">
</a>
</th>
<th>Path
<br />
<input type="search" ng-model="searchBuffer.Path">
</a>
</th>
<th>Name
<br />
<input type="search" ng-model="searchBuffer.Name">
</a>
</th>
<th>Description
<br />
<input type="search" ng-model="searchBuffer.Description">
</a>
</th>
</tr>
<tr ng-repeat="ComponetOwner in filtered | startFrom:currentPage | orderBy:predicate:reverse | limitTo:itemsPerPage">
<td>{{ComponetOwner["Technical Owner"]}}</td>
<td>{{ComponetOwner.Branch}}</td>
<td>{{ComponetOwner["Sub Pillar"]}}</td>
<td>{{ComponetOwner.Path}}</td>
<td>{{ComponetOwner.Name}}</td>
<td>{{ComponetOwner.Description}}</td>
</tr>
</table>
<pagination items-per-page="itemsPerPage" total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></pagination>
</div>
When I type something in the search box, $watch doesn't get called. What's going on?
searchBuffer is an object. The third optional argument of $watch needs to be set to 'true' for watching objects/arrays (that is for deep watching).
Read this:
$watch an object
You can do $watchCollection which will watch all the objects within an object.
Not as deep as setting the third optional argument which is true for yout $watch function.
Here is a good blog about it: http://www.bennadel.com/blog/2566-scope-watch-vs-watchcollection-in-angularjs.htm
Related
I am attempting to follow a JSFiddle, where a user can click on a <td> item, edit it, then eventually be able to save the changes.
The example uses ng-repeat and all others I have looked at do to where as I am not, I am using data passed from a resolve command in my route folder.
$stateProvider
.state('app.patents.patent', {
url: '/{patentId}',
component: 'patent',
resolve: {
patent: ['patents', '$stateParams', function(patents, $stateParams) {
return patents.find(function(patent){
return patent.id == $stateParams.patentId;
})
}]
}
})
}]);
I have attempted to use data-id (looked at How to retrieve the clicked ElementId in angularjs?), but with no success, as I assume you cannot use the same id twice and my desired functionality requires two elements that ng-show and ng-hide depending on the boolean value passed to them.
I have now got myself in a confused state, not sure which approach to take.
Question
How do I adapt my code that doesn't use ng-repeat to work with this JSFiddle? OR do you know another apporach I can take to achieve the same results?
<tr>
<th class="text-xs-right">Short Name</th>
<td>
<span data-id="123" ng-hide="$ctrl.shortTitle.editing" ng-dblclick="$ctrl.editItem(123)">{{$ctrl.patent.shortTitle}}</span>
<input type="text" data-id="123" ng-show="$ctrl.shortTitles.editing" ng-blur="$ctrl.doneEditing(123)" ng-model="$ctrl.patent.shortTitle"></input>
</td>
</tr>
angular.module('myApp').component('patent', {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm',
controller: function() {
var vm = this;
vm.editItem = function (item) {
item.editing = true;
}
vm.doneEditing = function (item) {
item.editing = false;
};
});
As per my understanding regarding your question I have created a jsfiddle, have a look or you can create a jsfiddle with the issue you are facing for better understanding
JSFiddle
<!DOCTYPE html>
<div ng-app ng-controller="myCtrl" class="container">Double-click on the items below to edit:
<button type="button" ng-click="newItem()">Add item</button>
<table>
<tr ng-repeat="item in items">
<td>
<span ng-hide="item.editing" ng-dblclick="editItem(item)">{{item.name}}</span>
<input ng-show="item.editing" ng-model="item.name" ng-blur="doneEditing(item)" autofocus />
</td>
</tr>
</table>
</div>
You can create an array and connect each input to a specific index starting from 0 and then pass that index to your function call.
<tr>
<th class="text-xs-right">Short Name</th>
<td>
<span ng-hide="$ctrl.editing[1]" ng-dblclick="$ctrl.editItem(1)">{{$ctrl.patent.shortTitle}}</span>
<input type="text" data-id="123" ng-show="$ctrl.editing[1]" ng-blur="$ctrl.doneEditing(1)" ng-model="$ctrl.patent.shortTitle"></input>
</td>
</tr>
angular.module('myApp').component('patent', {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm',
controller: function() {
var vm = this;
vm.editing=[];
vm.editItem = function (index) {
vm.editing[index] = true;
}
vm.doneEditing = function (index) {
vm.editing[index] = false;
};
});
Demo: http://jsfiddle.net/F7K63/381/
UPDATE: It was because not setting the values for $scope.quantity
I have 3 places where I should ng-repeat 3 different data sources.
I use 3 controllers, from which only the 1st one displays data. The other two can console.log the data, but do not display it.
I really can't understand why everything works for the 1st set of data, but not for the rest, even though the code is pretty much the same for all of them.
js
//this is working
function publisherController($scope, $http) {
//$scope.sortType = 'name'; // set the default sort type
//$scope.sortReverse = false; // set the default sort order
$scope.searchPublisher = ''; // set the default search/filter term
$http.get("/ServiceProxy.aspx?apiPath=api/path1")
.then(function (response) {
$scope.pubNames = response.data;
console.log(JSON.stringify($scope.pubNames));
});
$scope.quantity = 5;
};
// this is not working
var formatController = function ($scope, $http) {
//$scope.sortType = 'name'; // set the default sort type
//$scope.sortReverse = false; // set the default sort order
$scope.searchFormat = ''; // set the default search/filter term
$http.get("/ServiceProxy.aspx?apiPath=api/demand/path2")
.then(function (response) {
$scope.formatNames = response.data;
console.log($scope.formatNames);
});
};
//this is not working
function distributorController($scope, $http) {
//$scope.sortType = 'name'; // set the default sort type
//$scope.sortReverse = false; // set the default sort order
$scope.searchDistributor = ''; // set the default search/filter term
$http.get("/ServiceProxy.aspx?apiPath=api/path3")
.then(function (response) {
$scope.distributorNames = response.data;
console.log(JSON.stringify($scope.distributorNames));
});
};
html
<div class="row" ng-app>
<-- This is working -->
<div ng-controller="publisherController">
<table class="table table-bordered table-striped">
<tbody id="format">
<tr ng-repeat="roll in pubNames | orderBy:sortType:sortReverse | filter:searchPublisher | limitTo:quantity">
<td><input type="checkbox" id="myCheck">{{roll}}</td>
</tr>
</tbody>
</table>
</div>
<-- This is NOT working -->
<div ng-controller="formatController">
<table class="table table-bordered table-striped">
<tbody id="format">
<tr ng-repeat="f in formatNames | orderBy:sortType:sortReverse | filter:searchDistributor | limitTo:quantity">
<td><input type="checkbox" id="myCheck">{{f}}</td>
</tr>
</tbody>
</table>
</div>
<-- This is NOT working -->
<div ng-controller="distributorController">
<table class="table table-bordered table-striped">
<tbody id="distributor">
<tr ng-repeat="d in distributorNames | orderBy:sortType:sortReverse | filter:searchDistributor | limitTo:quantity">
<td><input type="checkbox" id="myCheck">{{d}}</td>
</tr>
</tbody>
</table>
</div>
</div>
Having trouble setting clearing input field
I’m currently trying to clear the input box on ng-model="newInstruction.instructionText" after an new instruction text has been added. Here is the code where I tried to reset it to empty string but input field didn't clear. Why is that?
I’ve tried console logging the following in updateInstructionText function:
Console.log (newInstruction) returns undefined in console.
Console.log ($scope.newInstruction) returns undefined in console.
Console.log (instruction) returns a object with instruction text inside ex: Object {instructionText: ‘new instruction’}
Controller:
angular.module('app').controller('InstructionController', function($scope, $http, NgTableParams, $filter) {
$scope.initList = function() {
$scope.getRemoteInstructions = function(typed) {
$http.get("/api/instructions?searchInstructionText=" + typed).then(function(response) {
$scope.instructionChoices = _.map(response.data, function(instruction) {
instruction.instructionText;
});
});
};
$scope.updateInstructionText = function(instruction) {
var instructionPromise;
if (instruction.id) {
instructionPromise = $http.put("api/instructions/" + instruction.id, instruction);
}
else {
instructionPromise = $http.post("/api/instructions", instruction);
}
$scope.newInstruction = '';
$instruction = '';
instructionPromise.then(function(response) {
$('#instructionModal').closeModal();
$scope.instructionTable.reload();
});
};
};
});
HTML
<div class="container-fluid" ng-init="initList()">
<div class="row">
<h3>Total Instructions: {{totalInstructions}}</h3>
<table class="striped highlight bordered" ng-table="instructionTable" show-filter="true">
<tr>
<td class="input-field">
<input placeholder="Enter New Instruction Text" ng-model="newInstruction.instructionText" type="text">
</td>
<td>
<a class="waves-effect waves-light btn" ng-click="updateInstructionText(newInstruction)">Add</a>
</td>
</tr>
</table>
</div>
</div>
<ng-include src="'/views/modals/instructionModal.html'"></ng-include>
</div>
Simply do:
instruction.instructionText = null
Since you are accessing the argument (object) inside the function with name instruction, if you set this to null, you can clear the input field
Simply do
$scope.newInstruction.instructionText=null
I have this input field in my html:
<input type="text" spellcheck="false" id="widgetu1049_input"
name="custom_U1049" tabindex="3" placeholder="Search..." ng-model="searchText"
ng-change="getPostHttp()" ng-trim="false"/>
and i'm calling http post in a scope function:
$scope.getPageItems = function(callback){//TODO add county and state moudles
var search = {'searchText':$scope.searchText,'state' : $scope.currentState,'county' : ''};
var params = {'action':'getPageItems', 'currentPage':$scope.currentPage, 'pageSize':$scope.pageSize, 'search':search };
$http.post(EndPoint, params).then(function(response) {
var page=response.data;
console.log(page);
callback(page);
});
}
I'm calling the above function from this function:
$scope.getPostHttp = function(){
$scope.getPageItems(function(data) {
$scope.items = data;
});
}
I've got this approach from this question Angular $http returns data but doesn't apply to scope
And although it shows the items on an ng-init call I made, it does not update on the ng-change call above.
Any ideas?
EDIT: I'm adding the view of the ng-repeat call:
<tr style=" background-color: #BFBFBF;" ng-model="items" ng-class="{marked: isExists(item.id) == true}" ng-click="view(item.id)"
data-toggle="modal" data-target="#smallModal" ng-repeat="item in items" ng-animate="'animate'">
<td ng-show="id">{{item.id}}</td>
<td ng-show="fname">{{item.fname}}</td>
</tr>
I am writing some functions for check/uncheck all for table list and it is working fine,
Controller is,
invoiceApp.controller('itemController', ['$scope', 'itemService', '$route', function ($scope, itemService, $route) {
$scope.checkAllItem;
$scope.listItem = {
selected: []
};
$scope.checkUncheck = function () {
if ($scope.checkAllItem) {
$scope.listItem.selected = $scope.items.map(function (item) {
return item.id;
});
} else {
$scope.listItem.selected = [];
}
};
HTML TABLE,
<table id="dt_basic" class="table table-bordered table-hover" width="100%">
<thead>
<tr>
<th class="text-center" width="5%">
<input type="checkbox" name="checkbox-inline" ng-model="checkAllItem" ng-click="checkUncheck()">
<input type="checkbox" name="checkbox-inline" ng-click="uncheckAll()">
</th>
<th width="15%" ng-click="sort()">Name<i class="fa fa-sort small"></i></th>
<th width="65%">Description</th>
<th width="5%">Unit</th>
<th width="10%">Rate</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items" data-toggle="modal" data-target="#itemModel" ng-click="getItem(item.id)" style="cursor: pointer">
<td class="text-center">
<input type="checkbox" checklist-model="listItem.selected" checklist-value="item.id">
</td>
<td><a>{{item.name}}</a></td>
<td>{{item.description}}</td>
<td>{{item.unit}}</td>
<td>{{item.rate}}</td>
</tr>
</tbody>
</table>
It is working fine,Here my problem is,In my project I have many tables in different pages,I have to copy past this same code (Talking about Controller only ) to everywhere.Is there any method to write it generally?
I tried with $routescope,
but It is not working with ng-model,Is there any method to implement the same?
You could turn it into a service then inject the service to whichever controller needs it. You can now also include other commonly used functions used to manipulate data in those. See,
http://jsbin.com/madapaqoso/1/edit
app.factory("toolService", function(){
return {
checkUncheck: function(listItem) {
listItem.selected = [];
}
}
});
I didn't add the added complexity of your function, but you get the idea.
Alternatively, use a directive. I show it in the jsbin as well. Though, I'd prefer a service since services are made for managing data and directives are more concerned with DOM editing and binding $watchers/events etc. Or perhaps you could persist the data with a service, then use a custom directive to handle all the clicks on the table.
I have written a custom directive
invoiceApp.directive('checkUncheck', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="checkbox" name="checkbox-inline" ng-model="checkAllItem" ng-click="checkUncheck()">',
link: function (scope) {
//check/uncheck and delete
scope.checkAllItem;
scope.listItem = {
selected: []
};
scope.checkUncheck = function () {
if (scope.checkAllItem) {
scope.listItem.selected = scope.items.map(function (item) {
return item.id;
});
} else {
scope.listItem.selected = [];
}
};
}
};
});
In HTML,
<check-uncheck></check-uncheck>
Now I can share checkUncheck function with most of table view in my project.