Is there a way to refresh virtual repeat container? - angularjs

I've got a md-virtual-repeat-container that handles the data from an API call triggered by a search box. I'd like to be able to refresh this container when a user enters a second search query.
I'm using a setup similar to this plnkr which is from this question:. The only difference is its getting data from a server when the user enters a search term.
My Question
Is there a way to trigger a refresh an md-virtual-repeat-container?
I don't think the code is relevant but here's my (simplified) production code:
var self = this;
self.infiniteItems = {
numLoaded_: 0,
toLoad_: 0,
items: [],
getItemAtIndex: function (index) {
if (index > this.numLoaded_) {
this.fetchMoreItems_(index);
return null;
}
return this.items[index];
},
getLength: function() {
return this.numLoaded_ + 25;
},
fetchMoreItems_: function (index) {
if (this.toLoad_ < index) {
this.toLoad_ += 5;
var offset = 0;
$http({
method: 'GET',
datatype: 'json',
url: '{my-api-call}',
contentType: "application/json; charset=utf-8",
cache: false,
params: {
param: query,
page: offset
}
}).then(angular.bind(this, function (obj) {
this.items = this.items.concat(obj.data.SearchResults);
this.numLoaded_ = this.toLoad_;
this.offset++;
$scope.searchResults = obj.data.SearchResults;
}));
}
}
};

You can force a redraw of your md-virtual-repeat container by triggering a fake window resize event (this causes the directive to call its private handleScroll_() method which then calls containerUpdated()).
This will trigger a fake window resize event:
angular.element(window).triggerHandler('resize');

You can also use this to cause refresh of items in specific scope and not in the entire document:
scope.$broadcast('$md-resize')

Resetting the model should also work.
In your case you could a have a new function on infiniteItems:
refresh : function() {
this.numLoaded_= 0;
this.toLoad_ = 0;
this.items = [];
}

I'm going to post my solution since there isn't an answer yet. If an better answer gets posted I'll gladly accept it.
First, I migrated all of my search result code into it's own template, directive and controller.
Next, I added an empty container in place of my logic. I'll use this as a place to dump my directive dynamically every time I search.
`<div id="search-result-container"></div>`
Finally, I dynamically appended my new directive like this
$scope.handleHotlineSearchClick = function () {
var query = $('#legal-search-input').val();
if (query != "") {
$scope.searchLoaded = false;
$('#search-result-container').empty().append($compile("<search-result></search-result>")($scope));
}
};
This way every time the user enters a new search term the directive gets reloaded.

Related

How to access the data after post in Angularjs

My Viewer
<button type="submit" ng:click="sendviewer(vl.viewerid)" ></button>
My Controller
$scope.viewerDetails = [];
var viewerid = [];
$scope.sendviewer = function(viewer){
var success = function(data)
{
for( var i = 0; i < 1; i++ )
{
var obj = data[i];
if(obj.viewerid != undefined) {
viewerid.push(obj.viewerid);
}
}
$scope.viewerDetails = viewerid;
}
viewerService.postViewer(viewer).success(success);
}
My Service
return {
postViewer: function(viewer){
$location.path('/viewerdetails');
return $http({
method: 'POST',
url: 'api.php',
data: {
'view': viewer
},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
}
}
My returned data is available in function memory but how will i be able to access that data outside the function to be accessible inside my view? if i try to do {{ viewerDetails.viewerid }} will display blank.
Well, it looks like the for-loop is just unnecessary, kind of weird actually.
$scope.viewerIds = [];
$scope.sendviewer = function(viewer){
var success = function(data)
{
var obj = data[0];
if(obj.viewerid != undefined) {
$scope.viewerIds.push(obj.viewerid);
}
}
viewerService.postViewer(viewer).then(success);
}
also you should not use success since it is deprecated. Use promise syntax with then instead.
This would add the id of the result of the call to the $scope.viewerIds array.
It is not entierly clear if this is what you want. but to me it looks like it.
Are you sure the data is like you expect it? an array where the first element is your object. This is also kind of weird api to be honest..
and then in your html you could perhaps loop the array to show the ids:
<div ng-repeat="id in viewerIds"> ID: {{id}}</div>
Since viewerid is a local array variable, the data stored inside it is not available outside the scope. You should store viewerid inside $scope. Like
$scope.viewerid = [];
Now it can be accessed on the DOM.

How can I get changed filter text for each column in Angular ui grid?

I m using new angular uigrid. But I think documentation is really poor for searching. I did not find necessary infos for filtering. I will use server side filtering . SO I have to get changed filter text for each column. Please help me?
I tried this code. But probably it was for ng grid(old grid)
$scope.$watch('completedgridOptions.filterOptions.filterText', function (newVal, oldVal) {
if (newVal !== oldVal) {
console.log("Filter");
}
}, true);
if you want to check what is what:
$scope.gridApi.core.on.filterChanged($scope, function () {
var grid = this.grid;
$.each(grid.columns, function (index, column) {
switch (column.field) {
case 'externalIdentifier':
$scope.identifier = column.filters[0].term;
break;
case 'name':
$scope.name = column.filters[0].term;
break;
case 'status':
$scope.status = column.filters[0].term;
break;
....
....
....
}
});
//you have to move it by hand, if you are on the third page and filter, it will stay on the third page, even though you will not have any data there
grid.options.paginationCurrentPage = 1;
//use the timeout so that you don't get a db call at every key pressed
if (angular.isDefined($scope.filterTimeout)) {
$timeout.cancel($scope.filterTimeout);
}
$scope.filterTimeout = $timeout(function () {
getPage(); //this will call your data from the db
}, 500);
});
var getPage = function () {
itemsPerPage = paginationOptions.pageSize;
offset = (paginationOptions.pageNumber -1) * paginationOptions.pageSize;
getDataByFilter($scope.name, $scope.identifier, $scope.status, offset, itemsPerPage)
}
I am new using angular as well but try this:
$scope.gridApi.core.on.filterChanged($scope, function () {
var grid = this.grid;
for (var i = 0; i < objGrid.columns.length; i++) {
term = objGrid.columns[i].filter.term;
field = objGrid.columns[i].field;
console.log('Field: ' + field + '\nSearch Term: ' + term);
}
});
$scope.gridOptions.data = $scope.data;//initialiazing grid with data
$scope.gridApi.core.on.filterChanged($scope, function () {
var grid = this.grid;
//do your stuff here
});
Basically I was able to use the filterchanged feature on grid only after the grid gets initialized with data.
So put the snippet as suggested in the thread only after initializing grid with data.
I have used $timeout as below for my convinience.
$timeout(function () {
$scope.getData();//get your data from ajax call or from where ever your data is
$scope.assignDatatoGrid();//assign the data to grid or initialize the grid with data
$scope.$apply();
//this feature is available only after the grid gets populated with data
$scope.gridApi.core.on.filterChanged( $scope, function() {
var grid = this.grid;
});
}, 2000);
It worked for me
Thank you

Programmatically Select AngularJS Typeahead Option

I have a AngularJS Typeahead that retrieves matches asynchronously. When a barcode is scanned into the field, it returns the matching result, but the user still has to select it. I would like to automatically select the result if it's an exact match. I see that the typeahead has a select(idx) function, but am not sure how to get a reference to it from my controller.
I was envisioning something like this:
$scope.SearchItems = function (term) {
return $http.get('api/Items/Search', {
params: {
term: term
}
}).then(function (response) {
if (response.data.length == 1 && response.data[0].Code == term) {
// Somehow inform typeahead control to select response.data[0]
}
return response.data;
});
};
I had a similar issue and never figured how to access the typeahead's select(idx), but I managed to get this functionality working. Here's my hacky workaround....
$promise.then(function(res) {
angular.forEach(res, function(item, key) {
// if we have an exact match
if (item.title === term) {
// update model
$scope.src = item;
// find item in dropdown
var elm = '[id*=option-' + key + ']';
var opt = angular.element(document.querySelectorAll(elm));
//call click handler outside of digest loop
$timeout(function() {
opt.triggerHandler('click');
}, 0);
}
});
// return async results
return res;
});
Basically we just update our model manually, locate the element in our dropdown and then fire off the 'click' handler. Make sure you wrap the triggerHandler call in a $timeout() set to zero, otherwise you will get a $rootScope:inprog error since digest is already in progress.

Passing current tab url to server in angular resources fetch

I am trying to send the current tab url to the resource service { in param } .
but the global tablUrl is not having any value at
var url = "http://[localhost]/getProfile?domain="+tabUrl
but getting logged corrent at :
console.log(tabUrl);
this is my code :
var tabUrl;
angular.module('jsonService', ['ngResource'])
.factory('JsonService', function($resource) {
chrome.tabs.getSelected(null, function(tab) {
tabUrl = tab.url;
console.log(tabUrl);
});
var url = "http://[localhost]/getProfile?domain="+tabUrl
return $resource(url,{}, {
list : {
method : 'GET',
cache : true
}
});
});
template binding :
<body ng-controller="extensionCtrl">
this is controller :
app.controller('extensionCtrl', function($scope , JsonService) {
JsonService.get(function(data){
$scope.data = data;
});
});
First:
Please, don't use the deprecated chrome.tabs.getSelected. Use chrome.tabs.query instead.
Second:
chrome.tabs.getSelected/chrome.tabs.query are asynchronous. This means that execution continues while they do some work in the background and the specified callback is called when they are done.
So, in a case like this:
line 1: chrome.tabs.getSelected(null, funkyCallback);
line 2: var url = ...
line 3: return $resource(...);
...a possible (and very probable) order of execution is:
1. chrome.tabs.getSelected (starts retrieving the active tab in the background)
2. line 2 gets executed (at this time 'tabURL' is not set yet)
3. line 3 gets executed (returning something)
4. Once the the active tab is retrieved, 'funkyCallback' is called
(setting 'tabURL' after it is too late).
When using asynchronous APIs (such as most of the chrome.* APIs), you have to change the whole logic of your scripts to be in line with the asynchronous nature of the API calls.
E.g., you could achieve the same result like this:
HTML:
<html ng-app="jsonService">
...
<body ng-controller="extensionCtrl">
<p>{{jsonData}}</p>
...
JS:
var app = angular.module("jsonService", ["ngResource"]);
app.factory("JsonFactory", function($resource) {
var url = "http://localhost/getProfile?domain=:tabUrl";
var retObj = $resource(url, {}, {
list: {
method: "GET",
cache: true
}
});
return retObj;
});
app.controller("extensionCtrl", function($q, $rootScope, JsonFactory) {
chrome.tabs.query({ active: true }, function(tabs) {
JsonFactory.list({ tabUrl: tabs[0].url }, function(data) {
// On success...
$rootScope.jsonData = data;
}, function(data) {
// On error...
$rootScope.jsonData = "Error using JsonFactory.list(...) !";
});
});
});
See, also, this short demo that does something similarly asynchronous

In Backbone.js when models are fetched in a loop ,success of the individual models are executed only at the end of the loop

Code of Collection Model and View
Backbone.Collection.extend({
model: xModel,
initialize: function(url) {
this.targetUrl = url;
},
url: function () {
return this.targetUrl;
},
parse: function (data) {
return data.instances;
}
}
Backbone.Model.extend({
defaults: {
location: '',
xyz: ''
},
initialize: function (options) {
},
url: function () {
return this.get('location');
}
}
}
renderInstances: function () {
this.collection = new xyzInstanceCollection(instPath);
this.collection.fetch({
success: _.bind(
function (data) {
for (var i = 0; i < this.collection.length; ++i) {
var model = this.collection.at(i);
model.fetch({
success: _.bind(
function (data) {
/*Call a subView to run the data over a template
and display the instance fetched*/
}, this),
error:
function (e, response) {
/*Do Something else*/
}
});
}
}, this),
error:
function (e, response) {
/*Do Something*/
}
}
});
}
Issue 1:
Now I was expecting that the views of the instances that are called from the success callback will start rendering as soon as the fetch of that model gets the data, but what I see is Backbone.js first queues up all the ajax calls of the complete loop, then once all these calls have returned data , only then the views starts rendering,this happens irrespective of sync or async fetch, why is this so ?
Issue 2:
If a refresh button is pressed before all models are fetched from this loop, the browser returns error in communication. How to abort these calls when user wants to route to another page without caring to see the results, there can be 1000s of xhr's returned from the loop of model fetches, I cannot collect them all and execute abort on them, is there any better way , please suggest.

Resources