Angular ui-router templateProvider never called - angularjs

I need to pass a route parameter to the server responding with a template, so I'm trying to use a templateProvider per several articles/other stack overflow docs.
I'm not getting any javascript errors, but the following templateProvider method is never even executed. When the templateUrl property is not commented out, this route works fine.
$stateProvider
.state('org.contacts.add', {
url: '/add',
views: {
'org#': {
// templateUrl: '/templates/issues/add',
controller: 'ContactsAddController',
templateProvider: function($route, $templateCache, $http) {
var url = '/templates/' + $route.current.params.org + '/contacts/add';
$http.get(url, {cache: $templateCache}).then(function(html){
return html;
});
}]
}
}
})
After some experimentation, it seems the $route was causing trouble. Taking that out and using $stateParams at least fires this code/controller.
However, while I see the ajax call firing and the proper html response, it's never loaded to the view.
templateProvider: function ($stateParams, $http, $templateCache) {
var url = '/templates/contacts/add';
$http.get(url, {cache: $templateCache}).then(function(html){
return html;
});
}

I'm guessing you need to return a $promise for this to work. Check out the example used on the UI-Router wiki:
$stateProvider.state('contacts', {
templateProvider: function ($timeout, $stateParams) {
return $timeout(function () {
return '<h1>' + $stateParams.contactId + '</h1>'
}, 100);
}
})
Notice the line that begins with return $timeout(.... Have you tried returning the $promise that is created by doing $http.get()?

Related

Angular module config - provide parameter for templateURL

I am trying to use a parameter in my templateURL. here is relative code:
a = angular.module('newmodule', []);
a.config(['$stateProvider',
function ($stateProvider, $rootScope) {
$stateProvider
.state('body.new', {
url: 'new?panelid',
params: {
'panelid': function ($rootScope) {
return $rootScope.panelid;
}
},
templateUrl: function($stateParams){
return getBaseUrl() + 'newpage.aspx?panel_id='+ $stateParams.panelid;
},
controller: 'newController'
});
}]);
The navigation to this state is done like this:
var url = $state.href('body.new');
$window.open(url, '_blank');
from another state's controller.
I am checking what's been sent to the server and see that $stateParams.panelid is undefined. But, the interesting thing is if instead of returning $rootScope.panelid I return a value like '44' then it works fine. Can someone please explain what's exactly is wrong here.
Thanks

My ui-router resolve don't return anything without errors

I have configure my MongoDb and API REST and when i tried to connect it with my Angular application i think it can't resolve.
I m learning MEAN application on this tutorial.
This is my ui-router configuration.
var app = angular.module('flapperNews', ['ui.router']);
app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider,$urlRouterProvider){
$stateProvider
.state('home',{
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl',
resolve: {
postPromise: ['posts',function(posts){
return posts.getAll();
}]
}
})
.state('posts',{
url: '/posts/{id}',
templateUrl: '/posts.html',
controller: 'PostCtrl'
});
$urlRouterProvider.otherwise('home');
}
]);
And this one is my factory.
app.factory('posts', ['$http',function (){
var o = {
getAll: function(){
return $http.get('/posts').success(function(data){
angular.copy(data,o.posts);
});
}
};
return o;
}]);
And this is the return of /posts
curl http://localhost:3000/posts/
[{"_id":"564f63d0e1f4efce4e36d863","name":"test","link":"http://test.com","__v":1,"comments":["564f70adf3340cab52f9d117"],"upvotes":2}]
The result is a white page whitout error.
Can you help me ?
Inject $http in your factory like below.
app.factory('posts', ['$http',function ($http){ your code }])
The issue that you have here is with the $http service in your factory. You need to inject it in the function, like this:
app.factory('posts', ['$http',function ($http){
var o = {
getAll: function(){
return $http.get('/posts').success(function(data){
angular.copy(data,o.posts);
});
}
};
return o;
}]);
It the rest of the code is fine, it should work without any issues. Here is one modified and simplified plunker that usses $q to return a promise instead of the $http since I don't have an http endpoint to call.
One error I see is that you haven't returned a promise in your resolve. getAll() uses an async api and thus you will have to return a promise. So change
posts.getAll();
to
posts.getAll().then(function(data) {
return data.data;
})
in your resolve. However, I am not sure if this will solve your problem completely.

ui-router resolve is not working with the index page controller

I want to resolve some value before I load the first page of my application, but it kept telling me
Unknown provider: programClassSummaryProvider <- programClassSummary <- HomeCtrl
I pretty sure I did it correctly, because I did the same thing for any other controller and routing. but it is not working for my homepage controller.
It seems like it load the controller first, before it is resolved in the routing. Anything wrong with my code?
In routing.js
$stateProvider
.state('home', {
url: '/home',
controller: 'HomeCtrl',
controllerAs: 'vm',
templateUrl: 'index_main.html',
resolve: {
programClassSummary: ['GroupDataFactory', function (groupDf) {
return groupDf.getProgramClassSummary();
}]
},
ncyBreadcrumb: {
skip: true
}
});
in controller.js
angular
.module('issMccApp')
.controller('HomeCtrl', homeCtrl);
homeCtrl.$inject = ['$scope', '$location', '$state', '$auth', 'programClassSummary'];
/* #ngInject */
function homeCtrl($scope, $location, $state, $auth, programClassSummary) {
var vm = this;
vm.isAuthenticated = isAuthenticated;
vm.programClassSummary = programClassSummary;
if (!$auth.isAuthenticated()) {
$state.go('login');
return;
}
function isAuthenticated() {
return $auth.isAuthenticated();
}
}
in factory.js
function getProgramClassSummary(showAll) {
var query = "";
if (showAll)
query = APIConfigObj.base_url + '/api/group/infor/programclasssummary?all=1';
else
query = APIConfigObj.base_url + '/api/group/infor/programclasssummary';
return $http.get(query)
.success(function (result) {
return result;
})
.error(function (err) {
return err;
})
}
I'd say, we really have to distinguish the UI-Router state world, and angular itself. Reason why is clearly defined here (extracted $resolve from UI-Router API documentation):
$resolve
resolve(invocables, locals, parent, self)
Resolves a set of invocables. An invocable is a function to be invoked via $injector.invoke(), and can have an arbitrary number of dependencies. An invocable can either return a value directly, or a $q promise. If a promise is returned it will be resolved and the resulting value will be used instead. Dependencies of invocables are resolved (in this order of precedence)
from the specified locals
from another invocable that is part of this $resolve call
from an invocable that is inherited from a parent call to $resolve (or recursively
from any ancestor $resolve of that parent).
There is a wroking plunker, which uses this index.html
<body ng-controller="RootCtrl">
a summary for a root:
<pre>{{summary}}</pre>
<ul>
<li>home
<li>other
</ul>
<div ui-view=""></div>
So, here we use some RootCtrl, which won't go through state machine UI-Router, it is angular basic stuff
The root controller must be defined as
.controller('RootCtrl', ['$scope', 'GroupDataFactory', function ($scope, groupDf) {
$scope.summary = groupDf.getProgramClassSummary();
}])
For a state home, we can use different approach, in fact the same as above (simplifed version below)
.state('home', {
url: "/home",
templateUrl: 'tpl.home.html',
resolve: {
programClassSummary: ['GroupDataFactory', function (groupDf) {
return groupDf.getProgramClassSummary();
}]
},
controller: 'HomeCtrl',
})
And its controller is now able to consume the locals
.controller('HomeCtrl', ['$scope', 'programClassSummary', function ($scope, summary) {
$scope.summaryForHome = summary;
}])
Check it in action here

Lazy load template and controller in angular UI-Router

I am attempting to lazy load a controller and template in my UI-Router router.js file, but am having difficulty with the template.
The controller loads properly, but after that is loaded, we must load the template and this is where things go wrong.
After ocLazyLoad loads the controller, we resolve an Angular promise which is also included in the templateProvider. The issue is instead of returning the promise (templateDeferred.promise) after the file is done loading, the promise is returned as an object.
.state('log_in', {
url: '/log-in',
controller: 'controllerJsFile',
templateProvider: function($q, $http) {
var templateDeferred = $q.defer();
lazyDeferred.promise.then(function(templateUrl) {
$http.get(templateUrl)
.success(function(data, status, headers, config) {
templateDeferred.resolve(data);
}).
error(function(data, status, headers, config) {
templateDeferred.resolve(data);
});
});
return templateDeferred.promise;
},
resolve: {
load: function($templateCache, $ocLazyLoad, $q) {
lazyDeferred = $q.defer();
var lazyLoader = $ocLazyLoad.load ({
files: ['src/controllerJsFile']
}).then(function() {
return lazyDeferred.resolve('src/htmlTemplateFile');
});
return lazyLoader;
}
},
data: {
public: true
}
})
Ok, thanks for the responses, but I have figured it out.
.state('log_in', {
url: '/log-in',
controller: 'controllerJsFile',
templateProvider: function() { return lazyDeferred.promise; },
resolve: {
load: function($templateCache, $ocLazyLoad, $q, $http) {
lazyDeferred = $q.defer();
return $ocLazyLoad.load ({
name: 'app.logIn',
files: ['src/controllerJsFile.js']
}).then(function() {
return $http.get('src/htmlTemplateFile.tpl.html')
.success(function(data, status, headers, config) {
return lazyDeferred.resolve(data);
}).
error(function(data, status, headers, config) {
return lazyDeferred.resolve(data);
});
});
}
},
data: {
public: true
}
})
So, after some more reading, I realized I had an issue with my promises. We create one called lazyDeferred which is the one to be returned to templateProvider and is declared as a global variable. templateProvider waits for the promise to be fulfilled.
After we load our controller, we create an XHR/ $http request to retrieve the template file. $http.get is a promise so we can return that, $ocLazyLoad.load also is a promise so we can return that as well. Finally, we just need to resolve the lazyDeferred one and that I think balloons through the promises and resolves all of them.
I apologize if this was not very clear, I'm not 100% sure of how this works.
In case you'd like to lazily load the controller, I would suggest follow these detailed answers:
requirejs with angular - not resolving controller dependency with nested route
angular-ui-router with requirejs, lazy loading of controller
In case we need to load dynamically the HTML template, it is much more easier. There is an example from this Q & A
Trying to Dynamically set a templateUrl in controller based on constant
(the working plunker)
$stateProvider
.state('home', {
url: '/home',
//templateUrl: 'index5templateA.html', (THIS WORKS)
templateProvider: function(CONFIG, $http, $templateCache) {
console.log('in templateUrl ' + CONFIG.codeCampType);
var templateName = 'index5templateB.html';
if (CONFIG.codeCampType === "svcc") {
templateName = 'index5templateA.html';
}
var tpl = $templateCache.get(templateName);
if(tpl){
return tpl;
}
return $http
.get(templateName)
.then(function(response){
tpl = response.data
$templateCache.put(templateName, tpl);
return tpl;
});
},
You can check these as well:
Angular UI Router: decide child state template on the basis of parent resolved object
Angular and UI-Router, how to set a dynamic templateUrl

Using resolve in $routeProvider causes 'Unknown provider ...'

I am trying to do an asynchronous http request to load some data before my app loads and so I am using a resolve in $routeProvider which is an http request in my MainController. For some reason, I keep getting Error: [$injector:unpr] Unknown provider: appDataProvider <- appData where appData is where I do my http request. I am using AngularJS v 1.2.5.
Here is the code and two methods that I tried that both give the same error:
Method #1
MainController.js
var MainController = ['$scope','$location','appData',
function($scope, $location, appData){
console.log(appData.data);
}
];
MainController.loadData = {
appData: function($http, $location, MainFactory){
var aid = MainFactory.extractAid($location);
return $http({method: 'GET', url: URL_CONST + aid});
}
};
app.js
var app = angular.module('HAY', ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/', {
redirectTo: '/pages/alerts'
})
.when('/pages/:pageName', {
templateUrl: function(params) {
return 'views/pages/' + params.pageName + '.html';
},
controller: MainController,
resolve: MainController.loadData
})
.otherwise({
redirectTo: '/pages/alerts'
});
});
I tried changing the name in case it was a conflicting system reserved keyword but with no luck. For some reason, appData is never recognized
Method #2
I also tried changing it around like so:
app.js
var app = angular.module('HEY', ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/', {
redirectTo: '/pages/alerts'
})
.when('/pages/:pageName', {
templateUrl: function(params) {
return 'views/pages/' + params.pageName + '.html';
},
controller: MainController,
resolve: {
appData: ['$http', '$location','MainFactory', function($http, $location, MainFactory) {
var aid = MainFactory.extractAid($location);
return $http({method: 'GET', url: URL_CONST + aid});
}]
}
})
.otherwise({
redirectTo: '/pages/alerts'
});
});
MainController.js
var MainController = ['$scope','$location','appData',
function($scope, $location, appData){
console.log(resolvedData);
}
];
However, the result was exactly the same. Does this have something to do with angular 1.2.5 ?
Here is a working version from someone else
http://mhevery.github.io/angular-phonecat/app/#/phones
And here is the code:
function PhoneListCtrl($scope, phones) {
$scope.phones = phones;
$scope.orderProp = 'age';
}
PhoneListCtrl.resolve = {
phones: function(Phone) {
return Phone.query();
},
delay: function($q, $defer) {
var delay = $q.defer();
$defer(delay.resolve, 1000);
return delay.promise;
}
}
angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/phones', {templateUrl: 'partials/phone-list.html', controller: PhoneListCtrl, resolve: PhoneListCtrl.resolve}).
otherwise({redirectTo: '/phones'});
}]);
Here's an example of the code I've used in the application I'm working on, not sure it will help much because its not much different than how you have it already.
Routing
.when('/view/proposal/:id',{
controller : 'viewProposalCtrl',
templateURL : 'tmpls/get/proposal/view',
resolve : viewProposalCtrl.resolveViewProposal
})
Controller
var viewProposalCtrl = angular.module('proposal.controllers')
.controller('viewProposalCtrl',['$scope','contacts','details','rationale',
function($scope,contacts,details,rationale){
$scope.contacts = contacts;
$scope.details = details;
$scope.rationale = rationale;
// [ REST OF CONTROLLER CODE ]
});
// proposalSrv is a factory service
viewProposalCtrl.resolveViewProposal = {
contacts : ['$route','proposalSrv',function($route,proposalSrv){
return proposalSrv.get('Contacts',$route.current.params.id)
.then(function(data){
return data.data.contacts;
},function(){
return [];
});
}],
details : ['$route','proposalSrv',function($route,proposalSrv){
return proposalSrv.get('Details',$route.current.params.id)
.then(function(data){
return data.data.details;
},function(){
return {};
});
}],
rationale : ['$route','proposalSrv',function($route,proposalSrv){
return proposalSrv.get('Rationale',$route.current.params.id)
.then(function(data){
return data.data.rationale;
},function(){
return {};
]
}]
};
Now that I think about it, when I was developing my application I did have a problem and not sure why when I named my resolve function "resolve." This gave me a problem:
.when('/path',{
// stuff here
resolve : myCtrlr.resolve
})
but this did not:
.when('/path',{
//stuff here
resolve : myCtrlr.myResolveFn
})
Another Possibility
The only other thing I can think of, is that you're returning the promise from the $http call and then trying to use appData.data Try using the .then function or one of the other functions (.success,.error) to retrieve the information from the promise.
The problem was NOT due to previously using different version of AngularJS.
Here are the fixes using the code that I have above.
In app.js, you need to declare the controller as controller: 'MainController' and NOT as controller: MainController even though you have var MainController = app.controller('MainController', ....).
Second and biggest thing was that in my index.html I declared my controller already like so:
index.html
body ng-app="HEY" controller="MainController" /body
This was causing the whole Unknown provider error Apparently angular wont tell you that you have already declared the controller that you are using to do the resolve it and that that will cause a weird error that have nothing to do with the resolve.
I hope this helps someone who may have the same problem.
One thing I noticed in angular 1x docs is that YOU DO NOT SPECIFY THE RESOLVED PARAMETER AS AN ANNOTATED DEPENDENCY
So this:
.when('/somewhere', {
template: '<some-component></some-component>',
resolve: {
resolvedFromRouter: () => someService.fetch()
}
})
export default [
'$scope',
'someService',
'resolvedFromRouter'
Controller
]
function Controller($scope, someService, resolvedFromRouter) {
// <= unknown provider "resolvedFromRouter"
}
is wrong. You don't specify the resolved parameter as a dependency, in the docs:
For easier access to the resolved dependencies from the template, the resolve map will be available on the scope of the route, under $resolve (by default) or a custom name specified by the resolveAs property (see below). This can be particularly useful, when working with components as route templates.
So just do this instead:
.when('/somewhere', {
template: '<some-component></some-component>',
resolve: {
resolvedFromRouter: () => someService.fetch()
}
})
export default [
'$scope',
'someService',
Controller
]
function Controller($scope, someService) {
$scope.$resolve.resolvedFromRouter; // <= injected here
}

Resources