AngularJS ui-router resolve of REST resource - angularjs

I am working on an application that photographers can use to upload photos. The frontend is AngularJS and there is a RESTfull api backend.
Because of some issues and the fact that ui-router seems better then ngRouter, I decided to change the $routeprovider to $stateProvider of ui-router.
However, my resolve doesn't work anymore (I guessed it would break but I cannot find the solution to my situation).
So here is the original $routeprovider code:
.when('/photographer', {
templateUrl : '/static/partials/photographer/photographer_dash.html',
controller : 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos();
}
}
})
The PhotogService is a $resource service that has the following $resource objects:
return $resource(apiHost, {}, {
'ownPhotos': {
url: apiHost + '/photographer_own_photos/',
method: 'GET',
interceptor: ResponseInterceptor
}
});
In the controller I would then do the following (photogPrepService being injected because of the resolve):
var promise = photogPrepService;
promise.then(
function (data) {
$scope.ownPhotos = data.data.photos;
});
This all worked well and I would get the photos in the scope.
However as said with ui-router it doesn't work and I cannot seem to get it working...
According to the docs (https://github.com/angular-ui/ui-router/wiki#resolve) the following should work:
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos();
}
}
However, when the resolve is injected in the controller and I use console.log() to print the response, I get the following:
Resource(value)
I somehow cannot seem to get the values (JSON response {"photos": ...}) injected into the controller.. I tried various solutions that have been suggested here on stackoverflow and read guides and the API of ui-router, but I cannot wrap my head around what is going wrong... I hope someone can guide me in the right direction..
Thanks!

I think you can use the code below:
//the PhotogService will keep the same as before
//inject the 'PhotogService' into the controller photographerController
PhotogService.ownPhotos().then(function(data) {
$scope.ownPhotos = data.data.photos;
});
//instead of injecting the photogPrepService through 'resove', inject the PhotogService into the controller
//for the $stateProvider
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
}

You need to give a promise to the resolve, your ressource should look more like :
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos().$promise;
}
}
});
Or modify your ressources to make them promises.

Related

Controller is not getting call when its state is changing

I am using ui-router for routing my angular app. I have set the routing configuration but didn't want to use controller as syntax. I am using following syntax:
.state('blog',{
url: '/blog',
templateUrl: '/templates/blog.html',
controller: 'BlogController'
})
However the template is being called into my ui-view but I BlogController is not being called. I have used console.log() into my BlogController but I didn't see anything in my console. Here is my BlogController.js
app.controller('BlogController', function($scope, PostService,){
console.log(0);
PostService.getPost().then(function(post){
$scope.postModel = post;
});
console.log(1);
});
As you can see, I am using a service to call data using $http. Below is my PostService :
app.service('PostService', function ($http) {
this.getPost = function () {
return $http({
method : 'GET',
url : 'http://domain.com/wp-json/wp/v2/posts'
})
.then(function(response){
return response.data;
}, function(error){
return error;
});
};
});
I think this the problem related to the service call. I have read some post about resolve property in state method of ui-router. I have tried that but nothing has working. Can somebody please help me to get rid out of this ?
The declaration of the ui-router module is wrong
var app = angular.module('mySiteApp', [require('angular-ui-router')])
Should be,
var app = angular.module('mySiteApp', ['ui.router'])
Check this link for cors errors

Controller doesn't find resolve method

I'm trying to get some config settings from an API before loading my view so I don't get a billion interpolation errors from trying to use undefined data. The app doesn't crash because of the errors but I need to get rid of the errors nontheless.
I figured I'd use a resolve and wait for the config method to resolve before loading the view but I'm unable to get the data to my controller so that I can use it there. I've been looking at other questions and apparently this is the way you should do it.. What am I doing wrong exactly?
The issue is not in the api factory, the api.getConfig() returns a promise of the $http.get request to the external API just fine so I won't include the function snippet here.
In my routing:
.when('/:category/top_rated', {
templateUrl: 'views/pages/results.html',
resolve: {
'isAuth': ['fbRefs', function(fbRefs) {
return fbRefs.getAuthObj().$requireAuth();
}],
'getConfig': ['api', function(api) {
api.getConfig().then(function(data) {
return data;
});
}]
}
})
Then in my controller I inject it like this:
core.controller('MainCtrl', ['getConfig', function(getConfig) {
getConfig.then(function(data) {
console.log(data);
});
}]);
However this throws a $injector:unpr error. Why can't it find the resolve method?
Your controller should be declared in the router, otherwise the controller has no context where 'getConfig' is coming from, thus an injector error.
.when('/:category/top_rated', {
templateUrl: 'views/pages/results.html',
controller: 'MainCtrl',
resolve: {
'isAuth': ['fbRefs', function(fbRefs) {
return fbRefs.getAuthObj().$requireAuth();
}],
'getConfig': ['api', function(api) {
api.getConfig().then(function(data) {
return data;
});
}]
}
})

Angular UI-router and using dynamic templates

I'm trying to load a template file using a rootscope value as for it's name.
I have a init controller which sets the $rootScope.template to "whatever.html", then I have my route like this:
$stateProvider.state('/', {
url: '/',
access: 'public',
views: {
page: {
controller: 'HomeCtrl',
templateProvider: function($templateFactory, $rootScope) {
return $templateFactory.fromUrl('/templates/' + $rootScope.template);
}
}
}
});
But this doesn't work. It actually freezes the whole chrome so that I have to kill the process in order to stop it... I've also tried this with templateUrl but with no results.
So how could I use my dynamic template file with UI-router?
Similiar to your other question (in order I found them): Angular and UI-Router, how to set a dynamic templateUrl, I also created a working plunker to show how to. How it would work?
So, if this would be state call:
#/parent/child/1
#/parent/child/2
And these would be states:
$stateProvider
.state('parent', {
url: '/parent',
//abstract: true,
templateUrl: 'views.parentview.html',
controller: function($scope) {},
});
$stateProvider
.state('parent.child', {
url: '/child/:someSwitch',
views: {
// see more below
Then we can use this templateProvider definiton:
templateProvider: function($http, $stateParams, GetName) {
// async service to get template name from DB
return GetName
.get($stateParams.someSwitch)
// now we have a name
.then(function(obj){
return $http
// let's ask for a template
.get(obj.templateName)
.then(function(tpl){
// haleluja... return template
return tpl.data;
});
})
},
What we can see is chaining of async results:
// first return of promise
return asyncstuff
.then(function(x){
// second return of a promise once done first
return asyncstuff
.then(function(y){
// again
return asyncstuff
.then(function(z){
return ... it
}
}
}
And that's what the magical templateProvider can do for us... wait until all promises are resolved and continue execution with known template name and even its content. Check the example here. More about template provider: Angular UI Router: decide child state template on the basis of parent resolved object

Getting A Route Parameter for Resolve

I'm getting killed by my inability to grok Angular-UI UI-Router. I have a state defined as follows:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
// From examples for testing
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$state) {
return FamilyService.getMember($state.current.params.membersId);
}
},
controller: 'MemberController'
});
Since the docs say $stateParams aren't available, I'm using $state.current.params. Should be fine. Instead, I get dead air. I can't access the membersId to save my life.
To test my service and make sure it's not the problem, I hard coded in a membersId and the controller gets the memberDetails result as well as the simpleObj result. So, Service and Controller and working great.
Example of that hardcoded:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
// From examples for testing
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$state) {
return FamilyService.getMember('52d1ebb1de46c3f103000002');
}
},
controller: 'MemberController'
});
Even though the docs say you can't use $stateParams in a resolve, I've tried it anyway. It doesn't work either.
return FamilyService.getMember($stateParams.membersId);
How in the world do I get the membersId param to get passed into the FamilyService for the resolve?
I don't have much hair left to pull out; so, someone save me please.
I finally figured this out. It was quite simple and in the docs. Despite reading several times, I overlooked it each time. I needed to inject $stateParams into the resolve:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$stateParams) {
return FamilyService.getMember($stateParams.membersId);
}
},
controller: 'MemberController'
});
I still don't understand why the documentation says this is not possible.
Two Important $stateParams Gotchas
The $stateParams object is only populated after the state is activated
and all dependencies are resolved. This means you won't have access to
it in state resolve functions. Use $state.current.params instead.
$stateProvider.state('contacts.detail', { resolve: {
someResolve: function($state){
//* We cannot use $stateParams here, the service is not ready //
// Use $state.current.params instead *//
return $state.current.params.contactId + "!"
}; }, // ... })

AngularJS UI-Router scoping issues

I've got what I think is a scoping issue with angular ui-router, but I'm not quite sure.
angular.module('Meeting').controller('MeetingController', ['$scope', 'signalRService', '$stateParams', function ($scope, signalRService, $stateParams) {
$scope.setMeetings = function(meetings) {
$scope.meetings = meetings.map(function(meeting) {
return {
id: meeting.CategoryId,
name: meeting.MeetingName
};
});
$scope.$apply();
};
$scope.connectToSignalR = function () {
signalRService.connect();
signalRService.registerAddMeetingsClientMethod($scope.addMeetings);
};
$scope.requestMeetings = function() {
signalRService.requestMeetings($stateParams.departmentId);
};
$scope.connectToSignalR();
$scope.eventId = $stateParams.eventId;
}]);
Basically, my module is injected with a signalR service, and I register a callback on it to set meetings. I have a button on my view to tell the signalR service to fetch the meetings, which then calls the callback I just registered.
Now, all this works fine with ui-router, but only the first time the page is loaded. Here's my routing config:
angular.module('Meeting')
.config(
['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state("meeting",
{
url: "/meeting/:departmentId/",
templateUrl: '/home/meetingPage',
controller: "MeetingController"
})
.state("meeting.members",
{
url: "/members/",
templateUrl: "/home/memberspage",
controller: "MeetingMemberController"
})
.state("meeting.edit", {
url: "/meetingedit",
views: {
'meetingtime': {
templateUrl: '/home/timepage',
controller: 'MeetingTimeController'
},
'location': {
templateUrl: '/home/locationpage',
controller: 'MeetingLocationController'
}
}
});
}]);
When I load up a meeting state (i.e. mysite/meeting/3), all the signalR methods are called, the meeting model in the MeetingController is populated, and the data appears in the view.
When I navigate to another state (i.e. mysite/meeting/4), the signalR methods are still called, and the meeting model is populated, but then just disappears. If I manually refresh the page with F5, it starts to work again, but navigating to a different page stops everything working.
I'm thinking it's a scoping issue, because when I navigate to a different page, the meetings object is still populated from the previous page.
The way I was registering callbacks with a singleton signalR service was getting really cumbersome, and doesn't play well with ui-router, as I found out.
I switched to using promises, and everything works so much more elegantly. Basically, I have a method on my signalR hub that returns the object I want:
public List<Meeting> GetMeetingsForMember(int memberId)
{
return _meetingRepository.GetAllUpcomingMeetingsForMember(int memberId);
}
Then, in my controller, I create a promise, and pass it to my signalR service for resolution:
var deferred = $q.defer();
deferred.promise.then(
function (meetings) {
setMeetings(meetings);
}
);
signalRService.getMeetingsForMember(memberId, deferred);
The getMeetingsForMember method on my signalR service accepts the promise and resolves it:
getMeetingsForMember = function (memberId, deferred) {
deferred.resolve(signalRService.hub.server.getMeetingsForMember(memberId));
}

Resources