I have a simple resolve function set up in my route provider. Something like
.config(['$routeProvider', 'Constants', function config($routeProvider, Constants) {
$routeProvider.
when('mypage', {
templateUrl: 'partials/mypage.tpl.html',
controller: 'MyPageCtrl',
reloadOnSearch: false,
resolve: {
factory: 'MyPageResolve'
}
})
)];
Where that factory is declared in a module like this:
.factory('MyPageResolve', ['$q', '$log', 'someAPI',
function ($q, $log, someAPI) {
var defer = $q.defer();
var postUserFunc = function () {
someAPI.get(
function(data, status) { //$http.get().success() func
defer.resolve();
}
);
};
return defer.promise;
}]);
What's happening is on straight refresh or on the first load, this resolve method is hit. However, if I have a $location.url('/mypage'); change from one route to this one, angular is not re-resolving this controller.
I might be not thinking of resolve in the right way, and if so, would take recommendations on the 'angular' way to do this. I would like all this information loaded before the view, and is variable based on the route params.
Ends up I needed to make that factory into just a function. A factory acts as a singleton, so it will only run once.
So I made my factory look like:
var myPageResolve = ['$q', '$log', 'someAPI',
function ($q, $log, someAPI) {
var defer = $q.defer();
var postUserFunc = function () {
someAPI.get(
function(data, status) { //$http.get().success() func
defer.resolve();
}
);
};
return defer.promise;
}]);
And instead of resolving on 'myPageResolve', I resolved on myPageResolve. Very subtle change made all the difference.
Related
I wrote some custom routing in my app configuration file to handle routing of all my html templates and controllers instead of having to specifically define the route for every single html and controller. I have a Registration.html and RegistrationController.js under my Modules/Account/ directory. My app can find the controller the first time I go to it and I can fill out the page and submit the form on the page. After I submit successfully, I get redirected to a success page. When I try to go back to the same registration html/controller the 2nd time, it can find my html template, but it can not find my controller anymore and i get the error "Argument 'RegistrationController' is not a function, got undefined". Can anyone tell me why and how to fix this?
Please note this error only happens after a form submit. If I leave the page and go back to it without doing a form submit, everything works fine.
App Config
define(['angularAMD', 'angular-route', 'ui-bootstrap', 'ui-grid'], function (angularAMD) {
var app = angular.module("MyApp", ['ngRoute', 'ui.bootstrap', 'ui.grid']);
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider
.when("/", angularAMD.route({
templateUrl: function (rp) { return 'Modules/Account/login.html'; },
controllerUrl: 'Modules/Account/LoginController'
}))
.when("/:module/:page", angularAMD.route({
templateUrl: function (rp) { return 'Modules/' + rp.module + '/' + rp.page + '.html'; },
resolve: {
load: ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) {
var path = $location.path();
var parsePath = path.split('/');
var parentPath = parsePath[1];
var controllerName = parsePath[2];
var loadController = "Modules/" + parentPath + "/" + controllerName + "Controller";
debugger;
var deferred = $q.defer();
require([loadController], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
}))
.otherwise({ redirectTo: '/' });
}]);
loadDirectives(app);
angularAMD.bootstrap(app);
return app;
});
RegistrationController
define(['app-config','accountService'], function (app) {
app.register.controller('RegistrationController', ['$scope', '$rootScope', '$location', '$uibModal', 'accountService',
function ($scope, $rootScope, $location, $uibModal, accountService) {
$rootScope.applicationModule = "Account";
$scope.registerUserSuccess = function (response, status) {
debugger;
$location.path("/Account/RegistrationSuccess");
}
$scope.registerUserFailure = function (response, status) {
if (!response.ValidationErrors) {
$scope.ErrorList = [];
$scope.ErrorList.push("An error occurred. Please contact the system's administrator");
}
else {
$scope.ErrorList = response.ValidationErrors;
}
}
$scope.onSubmitClick = function (isValid) {
if (isValid) {
accountService.registerUser($scope.regModel, $scope.registerUserSuccess, $scope.registerUserFailure);
}
}
$scope.onCancelClick = function () {
$location.path("/Login");
}
}
]);
});
I haven't honed in on the answer yet, but I've noticed some refactoring that you should do that might get you closer to figuring out your problem ...
Stop using controllers. Use components, which were introduced in 1.5. These are superior in their reusability. They are more flexible in how you can use them and in what you can pass into them. The only controllers you should be using are the ones in components.
Stop using $scope or $rootScope for anything. Used named controllers. This is the default setting for a component.
Consider ui-router over angular-route. It's just a lot better.
What is loadDirectives(app) doing?
Put a log statement in your RegistrationContoller.js and just verify that it isn't being called more than once. If it thinks RegistrationController is undefined after previously being defined, it just feels like it's being defined more than once.
Is there anything fishy in accountService.registerUser? Is this function forwarding you to the success screen? That's kind of weird... seems to me that the accountService.registerUser should return a promise, and the onSubmitClick should resolve the promise and forward the user.
Try forwarding back to the RegistrationController at different points in the code, and try to narrow down the exact point that it becomes undefined. I think that you have some code running somewhere that you don't think you do.
I am trying to lazy load my controllers in angular via requirejs
.when('/', {
templateUrl: 'views/main.html',
resolve: {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var deferred = $q.defer();
// At this point, use whatever mechanism you want
// in order to lazy load dependencies. e.g. require.js
// In this case, "itemsController" won't be loaded
// until the user hits the '/items' route
require(['controllers/main'], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
});
This is my controller
define(['angular'], function (angular) {
'use strict';
var app = angular.module('someApp.controllers.MainCtrl', [])
.controller('MainCtrl', ['$scope', function ($scope) {
$scope.abc = "abc";
return app;
});
My view doesnt show the variable abc. Even though the view is rendering fine
<span>abc={{abc}}</span>
Hello ive got something like this
<body ng-app="app" id="ng-app" ng-controller="MasterCtrl">
and some views which need master controller to resolve some data
<div ui-view></div>
im using ui-router
how to pass resolve to masterctrl?
if i try something like doing the master state at '/' in state configuration i dont get master resolved when urling to children
So, you're resolving something in your controller(s).. [let's use 'user']
.state('myCoolState', {
url: baseURL + 'coolState/:id',
templateUrl: baseURL + 'Scripts/Templates/CoolStateView.html',
controller: 'coolStateController',
title: 'MyCoolState.com',
resolve: {
user : function (userService) {
return userService.getUser();
}
}
})
and it's working great.. but now you want to resolve user in your master controller..
What works for me is, with a factory like:
.factory("userService", function ($q, $http, baseURL) {
return {
getUser: function () {
var deferred = $q.defer();
$http({
url: baseURL + 'api/User/',
method: "GET"
})
.then(function (result) {
deferred.resolve(result.data.data);
});
return deferred.promise;
}
}
});
I just inject my userService into the master controller
MasterCtrl.$inject = ['$scope', '$location', '$rootScope', '$state', '$http', 'baseURL', 'userService'];
with my master controller defined as
var MasterCtrl = function ($scope, $location, $rootScope, $state, $http, baseURL, user) {
user.getUser().then(function() {
//stuff where you need access to user
//this won't happen until 'user' is resolved
});
};
It'd be nice to resolve user outside of the masterCtrl definition.. so it looked cleaner.. as far as I know you can't do that.
I am trying to use Angular's routing to resolve the necessary objects for the controller scope. I have read a few tutorials on how to do this but I still get an Unknown Provider error. The issue seems to be with project being injected into ProjectDetailCtrl.
app.js
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config( function ($interpolateProvider, $routeProvider) {
$routeProvider
...
.when('/project/:projectId', {
templateUrl : 'partials/_project_detail.html',
controller: 'ProjectDetailCtrl',
resolve: {
project: function ($route, MyService) {
return MyService.get('projects/', $route.current.params.projectId).then(function(data) {
console.log('VERIFY DATA: ', data);
return data;
});
}
}
controllers.js
.controller('ProjectDetailCtrl', function ($scope, project) {
$scope.project = project;
}
Edit
services.js
.factory('MyService', function ($http, $q) {
var MyService = {
...
get: function (items_url, objId) {
var defer = $q.defer();
$http({method: 'GET',
url: api_url + items_url + objId}).
success(function (data, status, headers, config) {
defer.resolve(data);
}).error(function (data, status, headers, config) {
defer.reject(status);
});
return defer.promise;
},
Edit 2
The issue is apparently not with the Service method, as this also produces the error:
app.js
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config( function ($interpolateProvider, $routeProvider) {
$routeProvider
...
.when('/project/:projectId', {
templateUrl : 'partials/_project_detail.html',
controller: 'ProjectDetailCtrl',
resolve: {
project: {title: 'foo'}
}
});
})
I can verify my resolve function is being returned properly, but Angular still complains that project is unidentified. What is this issue here? I have tried making my controllers into a module and passing that to the myApp module, but I still get the same Unidentified Provider issue for project.
Note: I am using Angular 1.2.9.
Edit 3: solution
So the issue was this line in my template:
<!-- WRONG: <div ng-controller="ProjectDetailCtrl">-->
<div>
<h2 ng-show="project">Project: <strong>{{ project.title }}</strong></h2>
</div>
Apparently the ng-controller directive cannot be use with resolve.
You forgot to add ngRoute as a dependency for your module. Thats why $routeProvider and $route service are undefined.
Update:
See this example. Problem was in ng-controller directive
You're returning the MyService.get, which is a promise, and then within the result of the promise, you're returning the data.... but to what?
You want to return the promise, but not the original promise... so you create your own promise using $q, and Angular will wait and resolve it for you before loading your route.
project: function ($route, MyService) {
var deferred = $q.defer();
MyService.get('projects/', $route.current.params.projectId).then(function(data) {
console.log('VERIFY DATA: ', data);
deferred.resolve(data);
});
return deferred.promise;
}
Or, assuming MyService.get returns a promise, you should be able to just do
project: function ($route, MyService) {
return MyService.get('projects/', $route.current.params.projectId);
}
And obviously inject $q into config
This question has been asked before, therefore it's a possible duplicate.
The top answer points out usage of controllers within markup to have caused this.
Search your codebase (markup or templates to be exact) for the term ng-controller.
Verify if there is a matching controller, as in this case ProjectDetailCtrl
Remove it from the markup
It solved the problem in my case.
I can't seem to wire this up properly. I'll list the appropriate pieces. My issue is accessing the injected resources. All of the dependent pieces are undefined when I try to reference them.
var app = angular.module('app', ['ngResource','ui.bootstrap', 'ngGrid','app.services', 'app.directives', 'app.controllers'
])
.config(['$routeProvider', function ($routeProvider) {
return $routeProvider.
when('/', { templateUrl: 'partials/transaction.view.html', controller: 'TransactionCtrl' }).
when('/about', { templateUrl: 'partials/about.view.html', controller: 'AboutCtrl' }).
when('/transaction', { templateUrl: 'partials/transaction.view.html', controller: 'TransactionCtrl' }).
otherwise({ redirectTo: '/' });
}])
.config(['$httpProvider', function ($httpProvider) {
return $httpProvider.responseInterceptors.push(['logger', '$rootScope', '$q',
function (logger, $rootScope, $q) {
var error, success;
success = function (response) {
$rootScope.$broadcast("success:" + response.status, response);
logger.log("success:" + response.status);
return response;
};
error = function (response) {
var deferred;
deferred = $q.defer();
$rootScope.$broadcast("error:" + response.status, response);
logger.log("error:" + response.status);
return $q.reject(response);
};
return function (promise) {
return promise.then(success, error);
};
}
]);
}])
.run(['$rootScope', 'logger', function ($rootScope, logger) {
return $rootScope.$on('$routeChangeSuccess', function (event, currentRoute, priorRoute) {
return $rootScope.$broadcast("" + currentRoute.controller + "$routeChangeSuccess", currentRoute, priorRoute);
});
}]);
...the controllers are here:
angular.module('pennyWatch.controllers', ['$scope', '$location','logger', 'ngGrid', 'transactionService']).
controller('TransactionCtrl', [function ($scope, logger, ngGrid, transactionService) {
//code here
}]).
controller('AboutCtrl',[function ($scope, logger) {
$scope.logEntries = logger.logEntries;
}]);
So none of the resources I specified are available (all undefined): '$scope', '$location','logger', 'ngGrid', 'transactionService'
Any light shed on this would be greatly appreciated!
Thanks
I'm pretty sure the syntax for a controller is:
.controller('TransactionCtrl', ['$scope', 'logger', 'ngGrid', 'transactionService', function ($scope, logger, ngGrid, transactionService) {
//code here
}]);
You first list what to inject, then as the last element of the array to define a function with parameters that will represent what's injected.
For instance you could even have:
.controller('TransactionCtrl', ['$scope', 'logger', 'ngGrid', 'transactionService', function ($s, logr, ngGrid, transServ) {
//code here
}]);
This is to allow for easy minification.
The alternative controller syntax uses the parameter names when choosing what to inject. And since minification usually involves shortening variable names, it's suggested you use the syntax above.
Alternative syntax:
.controller('TransactionCtrl', function ($scope, logger, ngGrid, transactionService) {
//code here
});
I think you are swapping module-loading with service-injection
when declaring the pennywatch.controllers module, you should invoke the angular.module function passing the module dependencies in brackets, not the services. many of the services you cannot access are in the ng module, for instance
service injection is applied at the controller level