AngularJs ui-router success transition get the controller - angularjs

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!

Related

$http Service Not Updating View

I have two controllers one nested inside another. ParentController has one object which I am using in ChildController as it is directly available in child. Now, in child, I am calling $http service and then updating this object. I think it should update the view.
I tried calling $scope.$apply() promise success function, but I think I don't really understand how to use this one. How to update the DOM without refreshing the page?
I also read about calling $http service inside $scope.$apply(). How to do that?
$scope.addVideo = function (data) {
console.log('scope tutorial', tutorial);
$http.post('/tutorials/' + tutorial.id + '/videos/', $scope.formData)
.then((response) => {
$window.tutorial.videos.push(response.data.video);
window.location = '/tutorials' + tutorial.id;
})
}
Now, In the UI, I am using TutorialsController which takes care of adding Tutorial. I mean it's a form and it only works with form. Then there is VideoController that displays vidoes in this tutorial.
<div ng-controller="TutorialsController">
// add video in this tutorial logic
<ol ng-repeat="vid in tutorial.videos" ng-controller="VideoController">
<li>{{vid.title}}</li><button>Delete</button>
</ol>
</div>
When the video is added by parent, I want to update child and when child VideoController removes a video with a delete button, I want to remove it from the parent scope.
$scope.addVideo = function (data) {
console.log('scope tutorial', tutorial);
$http.post('/tutorials/' + tutorial.id + '/videos/', $scope.formData)
.then((response) => {
̶$̶w̶i̶n̶d̶o̶w̶.̶t̶u̶t̶o̶r̶i̶a̶l̶.̶v̶i̶d̶e̶o̶s̶.̶p̶u̶s̶h̶(̶r̶e̶s̶p̶o̶n̶s̶e̶.̶d̶a̶t̶a̶.̶v̶i̶d̶e̶o̶)̶;̶
$scope.tutorial.videos.push(response.data.video);
̶w̶i̶n̶d̶o̶w̶.̶l̶o̶c̶a̶t̶i̶o̶n̶ ̶=̶ ̶'̶/̶t̶u̶t̶o̶r̶i̶a̶l̶s̶'̶ ̶+̶ ̶t̶u̶t̶o̶r̶i̶a̶l̶.̶i̶d̶;̶
})
}
Considering you don't have full code written, I took some guesses and prepared something for you. You should try NOT to use $scope.$apply unless you are absolutely sure that you need it, most of the time angular will trigger digest cycle for you so there's no need to use $scope.$apply. Main thing is you could use $scope.$emit to emit value of video you want to delete.
If you are not familiar with $emit check this for more details (you could also check $broadcast which will send event from top to bottom - opposite of $emit):
https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit
So, in your case put in your VideoController something like:
$scope.deleteVideo = function(video) {
$scope.$emit('deleteVideo', video);
};
And in your TutorialsController you will need to listen to changes similar to this - data parameter will have your video object from child controller:
$scope.$on('deleteVideo', (event, data) => {
$scope.tutorial.videos = $scope.tutorial.videos.filter(video=>!angular.equals(video,data));
});
This above will just delete video from $scope - so you will need to to add your $http logic.
I simplified your video I added someProperty on each video object and title which you use to display title, right now everything is there just to show how you can easily communicate between controllers via $emit/$broadcast.
Here's that simplified example on fiddle: https://jsfiddle.net/pegla/d1rb96us/2/
Also I would advise you to check this Todd Motto's styleguide and start using component based architecture. It's currently best AngularJS style guide online: https://github.com/toddmotto/angularjs-styleguide
Hope it helps.
Cheers!

change url and reload after $location.search()

Sounds simple but I'm kinda stuck here.
$scope.triggerFetch = function() {
$location.search("zipcode", 344343); // this just replaces it but I need a reload as well
// now do the GET request
}
I use angular just for the UI interactions, the rendering and the heavy lifting does the backend framework.
I can replace the url but I need a function for the reload, I don't want to use $location.path(), there must be something more elegant.
(From the comments)
You can either add ngRoute as a dependency which will cause a reload when the search parameters are changed.
Option 2 is to do something like the following:
$scope.$watchCollection($location.search(), function () {
$window.location.reload();
})

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;
}

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

How to implement callback in ngHistory in Pubnub?

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?

Resources