I am using ui-router and trying to use resolve with no success.
Here is my app.js state looks
.state('addInvestment', {
url: '/addUpdateInvestment',
templateUrl: 'js/angular/modules/investment/views/AddUpdateInvestment.html',
resolve: {
InvestmentTypes: ["investmentService", function (investmentService) {
console.log("Resolving dependency...");
return investmentService.getInvestmentTypes();
}]
}
})
It calls my service just fine and I know that service returns data just fine because I am using this service on several places in my application.
Here is my controller looks like:
angular.module('InvestmentModule')
.controller('AddUpdateInvestment',
['$scope', 'toaster', '$state', '$stateParams', '$wizard', 'investmentService', 'InvestmentTypes',
function ($scope, toaster, $state, $stateParams, $wizard, investmentService, InvestmentTypes) {
$scope.modalData = {};
$scope.modalData.investmentTypes = InvestmentTypes.items;
}]);
When I load page, I see the following in chrome:
You have to delete the comma.
Then, you have to load the controller after the resolve has done. Almost you can try to use that controller in your state, something like this.
.state('addInvestment', {
url:'/addUpdateInvestment',
controller: 'AddUpdateInvestment as aui',
templateUrl:'js/angular/modules/investment/views/AddUpdateInvestment.html',
resolve: {InvestmentTypes: ['investmentService', function(investmentService) {
console.log("Resolving dependency...");
return investmentService.getInvestmentTypes();
}]}, // this will crash if you dont specify other parameter to the state.
})
EDIT:
Check if this fix the problem
InvestmentTypes: ['investmentService', // use single quotes
EDIT 2:
It seems that the injection in the resolve is like this:
resolve: {
InvestmentTypes: function(investmentService) {
console.log("Resolving dependency...");
return investmentService.getInvestmentTypes();
}
}
Please, look at the wiki here
I think I found the problem.
In your resolve you have InvestmentTypes in quotation marks
resolve: {
'InvestmentTypes': ['investmentService', function(investmentService){
return investmentService.getInvestmentTypes();
}]
}
if you change it to be
resolve: {
InvestmentTypes: ['investmentService', function(investmentService){
return investmentService.getInvestmentTypes();
}]
}
It should work. I've forked your plunker, if you open up the dev console it should log it to the console
http://plnkr.co/edit/KOxgOqsGJcTD1URuHEOn?p=preview
Finally,
After so much of a struggle, I made it working. There was a problem in my service where after getting response, it was calling a method called handleResponse. But that method was a not a return type of factory.. I don't know how does it make any difference if I call it from resolve Vs call it as a regular service in controller. But it was for some reason breaking the resolve.
Here is my old service..
angular.module('InvestmentModule').factory('investmentService', ['$rootScope', '$http', '$q', function($rootScope, $http, $q){
return{
getInvestmentTypes:function()
{
return $http.get(this.webServiceUrl+'getAllInstitutionsForDisplay').then(handleResponse, handleError);
}
}
function handleSuccess(response)
{
return response.data;
}
function handleError( response ) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (
! angular.isObject( response.data ) ||
! response.data.message
) {
return( $q.reject( "An unknown error occurred." ) );
}
// Otherwise, use expected error message.
return( $q.reject( response.data.message ) );
}
}]);
With the new code, I added the code in the handleSucess in the method itself as seen below..
getInvestmentTypes:function()
{
return $http.get(this.webServiceUrl+'getAllInstitutionsForDisplay').then(function(response){return response.data}, handleError);
}
I might have to do similar thing for the handleError as well I believe..
BUT it works now beautifully for me.
Only the problem with this approach is that it is breaking the DRY approach.. Any suggestion to improve it will be appreciated..
Related
So, UI-router resolves were a thing of beauty in angular 1:
$stateProvider.state('myState', {
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
controller: function($scope, myVar){
$scope.myVar= myVarFromResolve;
if(true){console.log($scope.myVar}; //logs 'test'
}
How do I do the same with an Angular 1.5. component (example below)?
export default function FooComponent() {
let fooComponent = {
restrict: 'E',
templateUrl: 'foo.html',
controller: controller,
controllerAs: 'foo',
bindToController: true,
};
return landingComponent;
}
And the resolve...
.state('fooState', {
parent: 'someParent',
url: '/fooUrl',
template: '<foo></foo>',
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
I read a guide on how to do this, but I don't really understand it. Seems like the functionality isn't fully in place and that's a hack.
Looks like the answer is "Yes, kinda"...
I was given a work-around to this problem by a team member that works for angular-ui-router 0.2.18 (use npm list angular-ui-router --depth=0 to check your version quickly). It does work while using ES6 classes.
The work around is based on the fact that there is no (easy/known) way to get the variable passed into the controller, like you would in Angular 1 before component architecture introduced in 1.5. Instead, we create a service method that creates a promise (or returns an http call). Then you simply put the promise in the resolve. The promise method WILL return before the controller loads. So while you do not have a easy to use var, you have the data you need in a service.
resolve: {
preloadSomeVariableIntoAService: function (MyExampleService) {
return MyExampleService.presetSomeVariable();
}
//Note that 'preloadSomeVariableIntoAService' won't be used anywhere in our case
}
The service could be something like this:
export default class MyExampleService {
constructor( $q ) {
this.q = $q;
this.myVariableFromResolve = undefined;
}
presetStreamStatus(){
var deferred = this.q.defer();
return $http.get(someUrl).then(response) => {
this.myVariableFromResolve = response.data;
deferred.resolve(events);
});
return deferred;
};
};
You will then be able to get myVariableFromResolve from the service. I am not sure you have to use $q deferred, but it seems to be necessary since I am not simply returning the call but also setting the variable. Not sure. Hope this helps someone.
So to summarise I am using angular-ui router resolve function to retrieve state specific data. However it doesn't seem to full wait for the promise to resolve:
state('parent.ChildState', {
url: '/myUrl?param1¶m1',
templateUrl: 'views/list.view.html',
controller: 'MyController',
resolve: {
data: resolveData
}
}).
function resolveData($stateParams, Utils) {
var filters = Utils.getFilters($stateParams);
DataService.myDataObj = DataService.get(filters, function(result, headers) {
DataService.myDataObj = result;
});
return DataService.myDataObj;
// Note I have also tried returning directly the DataService.get call however this makes all the below console log statements as undefined (see below for the controller code to know what I mean). So I do the assignment first and then return that.
}
Now in the controller I had a function that executes on load like so:
function ExpensesController(DataService) {
$scope.viewData = DataService;
initData();
function initData() {
// this generally logs a ngResource and shows the full data obj on console
console.log($scope.viewData.myDataObj);
// this gets undefined on console
console.log($scope.viewData.myDataObj.someField1);
// this log fine, however why do I need to do promise
// resolve? should be resolved already right?
$scope.viewData.myDataObj.$promise.then(function() {
console.log($scope.viewData.myDataObj.someField1);
});
As your required data to resolve is async, you need to return a promise and add return statement inside your callback function.
function resolveData($stateParams, Utils) {
var filters = Utils.getFilters($stateParams);
return DataService.get(filters, function(result, headers) {
DataService.myDataObj = result;
return DataService.myDataObj
});
}
You can read ui-router resolve docs more about how resolver works and when they should return promise or pure values.
I don;t know if I have got your problem :), but here is what I feel is wrong
1) in the resolve return a promise, it should not be resolved
function resolveData($stateParams, Utils) {
var filters = Utils.getFilters($stateParams);
return DataService.get(filters);
}
2) In the controller you should inject the data that is declared in resolve not the DataService so your controller should be
function ExpensesController(data) {
$scope.viewData = data;
}
I have this piece of code:
.service('webSocket', function ($rootScope, socketFactory, CONFIG, $timeout) {
angular.element(document).ready(function () {
$rootScope.log('Waiting for connection...',CONSOLE_INFO);
});
And I am getting this error:
TypeError: $rootScope.log is not a function
This service is injected into this controller:
.controller('mainCtrl', function mainCtrl($scope, $rootScope, webSocket, myConsole ...
In which I have:
$rootScope.log = function (msg, type) { myConsole.log(msg,type); ... };
Can you tell me where is the problem? Or at least point me in the right direction? The reason I am using document ready function is because apart from logging messages to browser console (console.log) I use notifications for user (pNotify library) which needs to be called after DOM is loaded.
Sharing something between services using $rootScope should be considered generally as anti-pattern. If you don't have some different implementation of console for different controllers, you can do it Angular-way and perform all configurations in config block. Subscribing to document ready event in the service is also not a good idea (I would prefer to do it in run block), since in angular service is instantiated once it is first time required by any other service or controller or whatever. In order to have configurable service that may have different console implementation I would implement it using provider as follows:
angular.module('app',[]).
constant('console', console).
constant('PNotify', PNotify).
provider('myConsole', function() {
var log = angular.noop;
function MyConsoleFactory() {
return {
log: log,
debug: log
}
}
this.setLog = function(logImplementation) {
log = logImplementation
}
this.$get = [MyConsoleFactory];
}).
config(['myConsoleProvider', 'console', 'PNotify', function(myConsoleProvider, console, PNotify) {
myConsoleProvider.setLog(function(msg) {
console.log('[LOG] '+ Date.now() + ':\t' + msg);
new PNotify({
title: 'Title',
text: msg
});
});
}]).
run(['myConsole', '$document', function(myConsole, $document) {
$document.ready(function () {
myConsole.log('Waiting for connection...');
});
}]);
In this case you don't need any controller at all.
Plunker: http://plnkr.co/edit/aV9TIO07pnDs26xDBPtf?p=preview
That happens because service code runs before service was added to controller(where $rootScope.log method is defined). You can move $rootScope.log = function (msg, type) { myConsole.log(msg,type); ... }; into app.run(...) method and it will work.
So i have this really weird problem where i can't inject one factory into another, but only in one specific factory.
So i have the following code..
Controller:
angular.module('cheetah').controller("NewSprintController", ["$rootScope", "$scope", "$state", "EventService", "$modalInstance", "$timeout", "ErrorService", "NewSprintLogicService", "EventIdentifierService", function NewSprintController($rootScope, $scope, $state, evs, $modalInstance, $timeout, err, nsl, eis) {
$scope.Sprint = nsl.CreateSprint();
...
Where i inject my NewSprintLogicService, which i resolve as nsl. This works just fine, if we then look at the NewSprintLogicService, it looks like this:
angular.module("cheetah").factory("NewSprintLogicService", ["ApiService", function NewSprintLogicService(api) {
return {
CreateSprint: function () { return api.Sprint(); },
GetAll: function() {
...
Now, here i'm injecting my ApiService, and this is where it gets tricky. I'm getting an empty object injected. This pattern works fine with a different controller and logic service. The Sprint property is actually defined in the ApiService.
And just clarify, here is my ApiService:
angular.module("cheetah").factory("ApiService", ["$resource", function ApiService($resource) {
return {
UserStory: $resource("/api/UserStory/:userStoryId", { userStoryId: "#Id" }),
...
I'm not getting any errors, besides a "cannot read property from undefined" and "undefined is not a function", when trying to use the ApiService in the LogicService.
Indeed it helps to try and formulate problems as a Stackoverflow question. In this case it has dawned on me that i was missing a "new" in my LogicService, so it should be like this instead:
angular.module("cheetah").factory("NewSprintLogicService", ["ApiService", function NewSprintLogicService(api) {
return {
CreateSprint: function () { return new api.Sprint(); },
...
Simple mistake.
I saw some sample code here Delaying AngularJS route change until model loaded to prevent flicker
And straight away I though this was the right way to go, I need to have my controller LOAD only when a resolve is finished loading, normally most of the examples around tell you to put the code under resolve in the routeprovder as an inline function, but this sounds wrong. The controller needs it so why not have the controller implement the function to resolve. This sounded just what I was looking for ie. This seems to use the prototype pattern ??
function PhoneListCtrl($scope, phones) {
$scope.phones = phones;
$scope.orderProp = 'age';
}
PhoneListCtrl.resolve = {
phones: function(Phone, $q) {
// see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
var deferred = $q.defer();
Phone.query(function(successData) {
deferred.resolve(successData);
}, function(errorData) {
deferred.reject(); // you could optionally pass error data here
});
return deferred.promise;
}
}
Problem is I have my controller like so
'use strict';
angular.module('TestApp')
.controller('ItemsCtrl', function ($scope) {
});
So how do I apply a new function on the controller when my controller is declared inside a module ?
What I really need is something like
TestCtrl.resolve = {
items: function( $q) {
..........
return deferred.promise;
}
}
This then would allow me to have in my routeprovider..
when('/items', {
templateUrl: 'views/items.html',
controller: 'TestCtrl',
resolve: 'TestCtrl.resolve'}). // Need to use ' around resolve?
But I am confused how I would get this to work ?
I would really love any feedback, I am at a loss.
Not possible to define like 'TestCtrl.resolve' if you want to use resolve with .controller syntax then you have to define inline within route provider . The advantage of inline resolve in routeprovider is that you can reuse controller easily but using same controller and changing the logic in resolve function
You can also use a service:
resolve: {data : function(service) {
return service.getData();
}}