How to implement callback in ngHistory in Pubnub? - angularjs

When trying to retrieve the history message in the on event , the loading time is too long.
The spinner show and hides too fast. But the message is not yet loaded.
How can we calculate or get the exact time to make the history load?
$scope.limit = 100
PubNub.ngHistory( {
channel : $scope.channel,
limit : $scope.limit
});
$rootScope.$on(PubNub.ngMsgEv($scope.channel), function(ngEvent, payload) {
**ActivityIndicator.showSpinner();**
$scope.$apply(function(){
$scope.messages.push(payload.message);
});
$(".messages-wrap").scrollTop($(".messages-wrap")[0].scrollHeight);
**ActivityIndicator.hideSpinner();**
});

Thank you so much for trying out the PubNub AngularJS API! I'll try
to provide some help. There is a little bit of a difference between
the PubNub JS API and the PubNub AngularJS API in this case.
Background
Behind the scenes, The PubNub JS API history() method returns instantly,
and invokes the callback when the given "page" of history is retrieved.
The AngularJS API, in its quest for simplifying this interaction, does not
take a callback - instead, it calls $rootScope.$broadcast() for each message
in the returned history payload.
In this version of the AngularJS API, it's not currently possible to "get inside"
the 'ngHistory' method to provide a callback. However, there are 2 solutions
available to you: one has always been there, and the second one I just added
based on your feedback.
Solutions
1) See codepen here (http://codepen.io/sunnygleason/pen/afqmh). There is an "escape hatch" in the PubNub AngularJS API that lets you call the JS API directly for advanced use cases, called jsapi. You can call PubNub.jsapi.history({channel:theChannel,limit:theLimit,callback:theCallback}). The only thing to keep in mind is that this will not fire message events into the $rootScope, and you will need to call $rootScope.$apply() or $scope.$apply() to make sure any changes you make to $scope within the callback function are propagated properly to the view.
2) See codepen here (http://codepen.io/sunnygleason/pen/JIsek). If you prefer a promise-based approach, I just added an ngHistoryQ() function to version 1.2.0-beta.4 of the PubNub AngularJS API. This will let you write code like:
PubNub.ngHistoryQ({channel:theChannel,limit:theLimit}).then(function(payload) {
payload[0].forEach(function(message) {
$scope.messages.push(message);
}
});
You can install the latest version of the AngularJS SDK using 'bower install pubnub-angular'.
With either of these solutions, you should be able to display and hide your spinner
accordingly. The only difference is in #2, you'll want to write code like this:
var historyPromise = PubNub.ngHistoryQ({channel:theChannel,limit:theLimit});
showSpinner();
historyPromise.then(function(payload) {
// process messages from payload[0] array
hideSpinner();
});
Does this help? Let me know what you think. Thank you again so much for trying this out.

Can you programmatically turn the spinner on at history call time, and programmatically disable it at callback time?

Related

AngularJs ui-router success transition get the controller

I have created a service that is to run any http requests that have been requested. This runs in the ui-router onSuccess hook and looks like this:
$transitions.onSuccess({}, function() {
document.body.scrollTop = document.documentElement.scrollTop = 0;
console.log('%cwe have completed a transition', 'color: green;');
httpQueueService.process();
});
The problem with this is that the controller is initialized after this hook, so I can't capture the http requests without adding a timeout:
function process() {
$timeout(() => {
let t = angular.copy(queue);
console.log('processing', t, t.length);
queue.forEach((task) => invoke(task));
}, 500);
}
I know the timeout is only half a second and I could probably reduce that even futher, but it doesn't seem right.
It would be better if I could get the controller that has been initialized directly from the success hook.
Is there a way of doing this?
I did try by looking at the transition object in the success hook:
$transitions.onSuccess({}, function(transition) {
console.log(transition);
});
But that doesn't appear to have the controller there
What you are looking for is the 'resolve' option of the ui-router's states. You are trying to asynchronously load data (from your httpQueueService) to the new state's controller if I understood your description correctly. I suggest you take a look at the official ui-router documentation, especially the tutorials. Obviously you can skip most of it, so starting from the second: https://ui-router.github.io/ng1/tutorial/hellosolarsystem seems feasible for you.
Basically it handles the asynchronous nature of what you are doing - without adding any exact, hard-coded timeout.
Here is another great article which I found helpful when dealt with async queries on transitions: https://medium.com/opinionated-angularjs/advanced-routing-and-resolves-a2fcbf874a1c
I hope this helps, however, if you have any more specific issue then let us know!

using data from a callback from Ebay api in Angularjs

I am trying to use the ebay api in an angular.js app.
The way the api works by itself is to pass data to a callback function and within that function create a template for display.
The problem that I am having is in adding the data returned from the callback to the $scope. I was not able to post a working example as I didnt want to expose my api key, I am hoping that the code posted in the fiddle will be enough to identify the issue.
eBayApp.controller('FindItemCtrl', function ($scope) {
globalFunc = function(root){
$scope.items = root.findItemsByKeywordsResponse[0].searchResult[0].item || [];
console.log($scope.items); //this shows the data
}
console.log($scope.items); //this is undefined
})
http://jsfiddle.net/L7fnozuo/
The reason the second instance of $scope.items is undefined, is because it is run before the callback function happens.
The chances are that $scope.items isn't updating in the view either, because Angular doesn't know that it needs to trigger a scope digest.
When you use the Angular provided async APIs ($http, $timeout etc) they have all been written in such a way that they will let Angular know when it needs to update it's views.
In this case, you have a couple of options:
Use the inbuilt $http.jsonp method.
Trigger the digest manually.
Option number 1 is the more sensible approach, but is not always possible if the request is made from someone else's library.
Here's an update to the fiddle which uses $http.jsonp. It should work (but at the moment it's resulting in an error message about your API key).
The key change here is that the request is being made from within Angular using an Angular API rather than from a script tag which Angular knows nothing about.
$http.jsonp(URL)
.success($scope.success)
.error($scope.error);
Option 2 requires you to add the following line to your JSONP callback function:
globalFunc = function(root){
$scope.items = root.findItemsByKeywordsResponse[0].searchResult[0].item || [];
console.log($scope.items); //this shows the data
$scope.$apply(); // <--
}
This method tells Angular that it needs to update it's views because data might have changed. There's a decent Sitepoint article on understanding this mechanism, if you are interested.

Angularjs view not reflecting the data retrieved from pouchDB (in browser database)

Background:
I am building my offline application which uses AngularJS for UI and PocuhDB for locally storing the data retrieved from the server.
Issue:
The data retrieved from PouchDB is not getting rendered in the UI.
Controller:
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
});
return promise;
}
And then in the UI code I have the following :
<h1> {{data}}</h1>
I have debugged the code and everything seem to work fine. But the data is not getting displayed in the UI.
If I hard code a value to the data field then its getting rendered in the UI
$scope.data ="TEST";
This question is kind a old but I just came around it.
Issue is that Angularjs is based on so called digest cycles. When your model or view is changed digest cycle is triggered, watch for changes and update model or view respectively. It is so called two way data binding.
This digest cycle is not triggered periodically on some time base but on events instead. Those events are angular directives like ng-click, ajax calls $http or some other angular events like $timeout. You can find more information about digest here.
In general you should use those things when working with angular application to avoid such situations. In some cases its not possible however like in your case when getting data from DB. Digest cycle is not triggered and your view is not updated by angular.
Workaround for this is manually trigger $digest cycle. Way you have described:
if(!$scope.$$phase) {
$scope.$digest();
}
is working but considered as angular anti-patern and is discouraged by angular team, you should use:
$timeout();
instead. For more information see this answer.
I would maybe consider adding $timeout() call to hook for insert, update, delete hooks or events. Maybe pouchDB sync could be helpfull there.
The code you show seemed correct, maybe you can use console.log() to track the progress of the data. I think the problem might not in this layer. Maybe in the area where you wrapped getDataFromLocalDb(), track and find if the data have transfer to here, or where it disappeared.
The code started to work when i added the following :
if(!$scope.$$phase) {
$scope.$digest();
}
But i have no idea what magic does this code do.
It would be a great help if some some could advice.
The complete code that works now is :
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
if(!$scope.$$phase) {
$scope.$digest();
}
});
return promise;
}

How to use $resource in AngularJS properly for building a client app?

I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!

How to intercept AngularJS $http logging for display in page

I want to intercept console log message from AngularJS and display them in a div on the page. I need this in order to debug ajax traffic in a PhoneGap app.
This is an example of the kind of errors I want to capture:
I tried this Showing console errors and alerts in a div inside the page but that does not intercept Angular error messages.
I also tried the solution gameover suggested in the answers. No luck with that either. Apparently $http is handling error logging differently.
I guess the answer you tried has the right idea but you're overriding the wrong methods. Reading here I can see angularJs uses $log instead of console.log, so to intercept you can try to override those.
Something like this:
$scope.$log = {
error: function(msg){document.getElementById("logger").innerHTML(msg)},
info: function(msg){document.getElementById("logger").innerHTML(msg)},
log: function(msg){document.getElementById("logger").innerHTML(msg)},
warn: function(msg){document.getElementById("logger").innerHTML(msg)}
}
Make sure to run that after importing angular.js.
EDIT
Second guess, override the consoleLog method on the LogProvider inner class on angular.js file:
function consoleLog(type) {
var output ="";
//arguments array, you'll need to change this accordingly if you want to
//log arrays, objects etc
forEach(arguments, function(arg) {
output+= arg +" ";
});
document.getElementById("logger").innerHTML(output);
}
I've used log4javascript for this purpose. I create the log object via
var log = log4javascript.getLogger('myApp')
log.addAppender(new log4javascript.InPageAppender());
I then use this in a value dependency, and hook into it where needed (e.g. http interceptor).
A more lightweight approach might be to use $rootScope.emit and then have a component on your main page which prepends these log messages to a visible div, but this will require you to change all your calls to console.log (or redefine the function in your js).
I think that this message is not even displayed from AngularJS. It looks like an exception which has not been caught in any JavaScript (angular.js just appears on top of your stack because that's the actual location where the HTTP request is being sent).
Take a look at ng.$exceptionHandler. That should be the code you seem to be interested in. If not, take a quick web search for „JavaScript onerror“ which should tell you how to watch for these kinds of errors.
I would rather user an $http interceptor.
Inside the responseError function, you can set a property on a service that will be exposed to the div's scope.

Resources