Error: Unknown provider: employeesProvider <- employees - angularjs

I am having a heck of a time trying to figure out why I'm getting the Unknown provider error in Angular. I've checked every other question I could find on the subject and most suggest an error in dependency injection. However, it doesn't seem to me like I'm forgetting to inject anything. I've been trying to get the resolve property to work like this post by Misko. I'm able to console log out the employee data after it's resolved, but then I get the Unknown provider error, which prevents the data from being shown on the page.
Here is my router:
"use strict";
var app = angular.module('app',[
'employeeServices'
]);
app.config(appRouter);
function appRouter ($routeProvider) {
$routeProvider
.when('/employees/:account_id', {
controller: 'EmployeeCtrl',
templateUrl: 'view/employee/view.html',
resolve: employeeCtrl.resolve
})
.otherwise({ redirectTo: '/' });
}
Here is my controller
var employeeCtrl = app.controller('EmployeeCtrl', [
'$scope',
'employees',
function ($scope, employees) {
$scope.employee = employees;
console.log($scope.employee);
}
]);
employeeCtrl.resolve = {
employees: function (Employee, $q, $route) {
var deferred = $q.defer();
console.log("current params: ", $route.current.params.account_id);
Employee.getOne({ id: $route.current.params.account_id }, function (successData) {
deferred.resolve(successData);
}, function (errorData) {
deferred.reject(errorData);
});
return deferred.promise;
}
};
And my factory:
angular.module('employeeServices', ['ngResource'])
.factory('Employee', ['$resource', function ($resource) {
return $resource('/employees/:id/json',
{
id: '#account_id'
},
{
'save': {
method: 'POST',
isArray: false
},
'update': {
method: 'PUT',
params: {
id: '#account_id'
}
},
'remove': {
method: 'DELETE',
params: {
id: '#account_id'
}
},
'getOne': {
method: 'GET',
params: {
id: '#account_id'
},
isArray: false
},
'query': {
method: 'GET',
params: {
id: '#account_id'
},
isArray: true
}
}
);
}]);
Any suggestions would be so greatly appreciated!

So the problem was that I was setting up the EmployeeCtrl controller through ng-controller inside my partial's view, like so:
<div class="viewPage" ng-controller="EmployeeCtrl">
When using resolve, however, the controller set up must be done through the router in order for it to be available at runtime. I removed the ng-controller="EmployeeCtrl...
<div class="viewPage">
... and presto, like nothing ever happened.
I have to note that I received help from the kind, patient folks over on the AngularJS IRC channel...

Since you defined the factory called Employee, you should use the exact name to refer to this module when you inject it to the controller.
var employeeCtrl = app.controller('EmployeeCtrl', [
'$scope',
'employees',
Change to
var employeeCtrl = app.controller('EmployeeCtrl', [
'$scope',
'Employee',

Related

How to inject $stateParams into state resolve

I've been working on some code for a project and have run into a problem with using $stateParams. I am trying to inject a item name into my service through a resolve but it keeps coming up with undefined. I realized I need $stateParams to define it but I can't seem to inject $stateParams into the service. Here is my code and the error, thank you in advance.
Error
Uncaught Error: [$injector:modulerr]
http://errors.angularjs.org/1.6.5/$injector/modulerr?p0=MenuAppX&p1=ReferenceError%3A%20%24stateParams%20is%20not%20defined%0A%20%20%20%20at%20RoutesConfig%20(http%3A%2F%2Flocalhost%3A3000%2Fsrc%2Froutes.js%3A30%3A14)%0A%20%20%20%20at%20Object.invoke%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A44%3A357)%0A%20%20%20%20at%20d%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A42%3A237)%0A%20%20%20%20at%20http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A42%3A376%0A%20%20%20%20at%20p%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A8%3A7)%0A%20%20%20%20at%20g%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A42%3A138)%0A%20%20%20%20at%20gb%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A46%3A251)%0A%20%20%20%20at%20c%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A22%3A19)%0A%20%20%20%20at%20Uc%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A22%3A332)%0A%20%20%20%20at%20xe%20(http%3A%2F%2Flocalhost%3A3000%2Flib%2Fangular.min.js%3A21%3A1)
at angular.min.js:7
at angular.min.js:43
at p (angular.min.js:8)
at g (angular.min.js:42)
at gb (angular.min.js:46)
at c (angular.min.js:22)
at Uc (angular.min.js:22)
at xe (angular.min.js:21)
at angular.min.js:333
at HTMLDocument.b (angular.min.js:38)
State
.state('items', {
url: '/menuItems/{itemId}',
templateUrl: 'src/MenuApp/template/items.template.html',
params: {$stateParams, itemId: null},
controller: 'itemsXController as itemsX',
resolve: {
itemsResult: ['MenuDataService, $stateParams', function
(MenuDataService, itemId, $stateParams) {
return MenuDataService.getItemsForCategory($stateParams.itemId);
}]
}
})
Service
(function () {
'use strict';
angular.module('data')
.service('MenuDataService', MenuDataService)
.constant('categoryBasePath', "https://davids-
restaurant.herokuapp.com/menu_items.json?category=");
MenuDataService.$inject = ['$http', '$q', '$timeout','categoryBasePath'];
function MenuDataService($http, $q, $timeout, categoryBasePath) {
var service = this;
service.getAllCategory = function() {
var deferred = $q.defer();
var categoriesResult = $http({
method: "GET",
url: ('https://davids-restaurant.herokuapp.com/categories.json'),
});
$timeout(function () {
deferred.resolve(categoriesResult);
}, 800);
return deferred.promise;
}
service.getItemsForCategory = function(itemId) {
var deferred = $q.defer();
console.log(itemId);
var itemsResult = $http({
method: "GET",
url: ('https://davids-restaurant.herokuapp.com/menu_items.json?category=L'),
});
$timeout(function () {
deferred.resolve(itemsResult);
}, 800);
console.log(deferred.promise);
return deferred.promise;
}
};
})();
Template(the one calling the state)
<a ui-sref="items">This Page works</a>
<ol>
<li ng-repeat="item in categoriesX.categories" ui-sref="items({itemId:
categoriesX.categories[$index].short_name})">
{{ categoriesX.categories[$index].name }}
</li>
</ol>
remove itemid from the resolve function
change this
resolve: {
itemsResult: ['MenuDataService, $stateParams', function
(MenuDataService, itemId, $stateParams) {
return MenuDataService.getItemsForCategory($stateParams.itemId);
}]
}
to this
resolve: {
itemsResult: ['MenuDataService, $stateParams', function
(MenuDataService, $stateParams) {
return MenuDataService.getItemsForCategory($stateParams.itemId);
}]
}
.state('items', {
url: '/menuItems/{itemId}',
templateUrl: 'src/MenuApp/template/items.template.html',
params: { itemId: null},
controller: 'itemsXController as itemsX',
resolve: {
itemsResult: ['MenuDataService, $stateParams', function
(MenuDataService, itemId, $stateParams) {
return MenuDataService.getItemsForCategory($stateParams.itemId);
}]
}
})

Error handling when data is not resolved in Angular UI router

I am using angular-ui-router's resolve to get data from server before moving a state. I want to inform user if the request has failed. I have service that will response error the request has failed. My question is how can I detect the failure in ui-router resolve and trigger some Modal service like pop up.
I have read some related posts online, but I am still confused how to make it happen. Thanks in advanced!
Config and Service:
angular.module('myApp',['ui.router', 'ngAnimate', 'ui.bootstrap'])
.config(function ($stateProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider
.state('customer', {
url: '/customer',
templateUrl: '/customer.html',
controller: 'CustomerCtrl',
resolve: {
/*
* How to inject CustomerService here
* How to catch the server error
* How to trigger a popup
*/
data: cusomter_data
}
});
})
.service('CustomerService', function($http, $q) {
return ({
getGeneral: getGeneral
});
function getGeneral(customer_id) {
var request = $http({
method: "get",
url: '/customer',
params: {
customer_id: customer_id
}
});
return (request.then( handleSuccess, handleError));
}
function handleError (response){
if (!angular.isObject(response.data) || !response.data.message) {
return($q.reject("Failed to retrieve customer information."));
}
return($q.reject(response.data.message));
}
function handleSuccess(response) {
return (response.data);
}
});
After some research, I found out a solution by creating a errorModal service and inject it to resolve. Here is my solution.
$stateProvider
.state('customer', {
url: '/customer',
templateUrl: '/customer.html',
controller: 'CustomerCtrl',
resolve: {
data: function(CustomerService, SelectedCustomerService, errorModalService) {
var shared = SelectedCustomerService;
return CustomerService.getData(shared.customerid).then(
function (data) { return data; },
function(error) {
var modalOptions = {
closeButtonText: 'Close',
headerText: 'Customer Error',
bodyText: error
};
errorModalService.showModal({}, modalOptions);
}
);
}
}
});

Resolve multiple objects in angularjs ui-router state?

Here is my code:
.state("dashboard.userRoles", {
url: "/user/:id/roles",
controller: "userRolesController as vm",
templateUrl: "app/auth/users/user-roles.html",
resolve: {
user: function (userResource, $stateParams) {
return userResource.get({ id: $stateParams.id }).then(function (res) { return res.data; });
},
roles: function($http, $stateParams) {
var url = appSettings.authApiBaseUrl + "api/accounts/users/" + $stateParams.id + "/roles";
return $http.get(url).then(function(res) { return res.data; });
},
loadMyFiles: function($ocLazyLoad) {
return $ocLazyLoad.load({
name: "app",
files: [
"app/auth/users/userRolesController.js"
]
});
}
}
})
If I navigate to dashboard.userRole and look in fiddler, I see the request to get the user resource, but not the roles. If I comment out the user: section, I see the request to get the roles in fiddler. Why can't I resolve both? Should I just be sending the id into the controller and get everything there?
I was trying to avoid gathering data in the controller as it should just be the stitching between the view model stuff and the ui. Maybe that doesn't matter? Thanks in advance.
Edit 1:
Ok, I can change the code to this and see both requests showing in fiddler, and they both return the properly formatted json data:
.state("dashboard.userRoles", {
url: "/user/:id/roles",
controller: "userRolesController as vm",
templateUrl: "app/auth/users/user-roles.html",
resolve: {
user: function (userResource, $stateParams) {
return userResource.get({ id: $stateParams.id }).$promise;
},
roles: function($http, $stateParams) {
var url = appSettings.authApiBaseUrl + "api/accounts/users/" + $stateParams.id + "/roles";
return $http.get(url).then(function(res) { return res.data; }).$promise;
},
loadMyFiles: function($ocLazyLoad) {
return $ocLazyLoad.load({
name: "app",
files: [
"app/auth/users/userRolesController.js"
]
});
}
}
})
However, the roles injected into the controller are always 'undefined'. The user is populated correctly. And the response in fiddler shows the roles that come back, so I'm not sure why they are undefined. Here is the controller code.
"use strict";
angular
.module("app")
.controller("userRolesController", [
"user", "roles", function (user, roles) {
console.log("app.userRolesController.function()");
var vm = this;
vm.user = user;
vm.roles = roles;
}
]);
This angular-ui-router issue/question was helpful. Anyhow, this works!
.state("dashboard.userRoles", {
url: "/user/:id/roles",
controller: "userRolesController as vm",
templateUrl: "app/auth/users/user-roles.html",
resolve: {
user: function (userResource, $stateParams) {
return userResource.get({ id: $stateParams.id });
},
roles: function($http, $stateParams) {
var url = appSettings.authApiBaseUrl + "api/accounts/users/" + $stateParams.id + "/roles";
return $http.get(url);
},
loadMyFiles: function($ocLazyLoad) {
return $ocLazyLoad.load({
name: "app",
files: [
"app/auth/users/userRolesController.js"
]
});
}
}
})
And here is the controller. The .data on roles is important!
angular
.module("app")
.controller("userRolesController", [
"user", "roles", function (user, roles) {
var vm = this;
vm.user = user;
vm.roles = roles.data;
}
]);
Set a breakpoint (or watch) in the Chrome browser (within your angular controller). And inspect $state. I found my answer to be:
$state.$current.locals.globals.employeeslist
Thats pretty weird. Perhaps you could try:
.state("dashboard.userRoles", {
url: "/user/:id/roles",
controller: "userRolesController as vm",
templateUrl: "app/auth/users/user-roles.html",
resolve: {
// put both in one object
data: function (userResource, $stateParams, $http) {
var url = appSettings.authApiBaseUrl + "api/accounts/users/" + $stateParams.id + "/roles";
return {
user: userResource.get({ id: $stateParams.id }).then(function (res) { return res.data; });
roles: $http.get(url).then(function(res) { return res.data; });
}
},
loadMyFiles: function($ocLazyLoad) {
return $ocLazyLoad.load({
name: "app",
files: [
"app/auth/users/userRolesController.js"
]
});
}
}
})
Update:
Solution above doesn't fix the problem. OP has resolved his own issue (See the comments below).

Pass a request header to uriTemplate in AngularJS

I have this Angular code:
.state('UserTables', {
url: '/Tables',
resolve: {
auth: function resolveAuthentication(SessionService) {
return SessionService.isUser();
}
},
views: {
"containerMain": {
templateUrl: 'Views/Tables',
controller: TableController
},
}
})
And would like to pass some request header to the templateUrl call.
Anyone done something like that?
Basically I have a REST service that can generate the view I need depending on 1 header and some property's. Property's are no problem but I have no clue how to make a call to the service and wait for the result.
Tried:
views: {
"containerMain": {
template: function (SessionService, $http, $q) {
console.log('template');
var resp = SessionService.getTable($http, $q, 'Generate/Table?objectId=WfObject');
var r = '';
resp.then(function (result) {
r = result;
console.log('resp:', r);
});
console.log('r:',r);
return r;
}
I created working plunker here
To load template with custom headers, we can call do it like this (check the state 'UserTables' in the plunker):
views: {
"containerMain": {
//templateUrl: 'Views/Tables',
templateProvider: ['$http',
function ($http) {
var tplRequest = {
method: 'GET',
url: 'Generate/Table?objectId=WfObject',
headers: {
'MyHeaderKey': 'MyHeaderValue'
},
}
return $http(tplRequest)
.then(function(response) {
console.log('loaded with custom headers')
var tpl = response.data;
return tpl;
}
);
}],
controller: 'TableController'
},
}
In case, we want (and can) cache the template, we can do it like this (check the state 'UserTablesWithCache'):
views: {
"containerMain": {
//templateUrl: 'Views/Tables',
templateProvider: ['$http', '$templateCache',
function ($http, $templateCache) {
var templateName = 'Generate/Table?objectId=WfObject';
var tpl = $templateCache.get(templateName)
if(tpl){
console.log('returning from cache');
return tpl;
}
var tplRequest = {
method: 'GET',
url: templateName,
headers: {
'MyHeaderKey': 'MyHeaderValue'
},
}
return $http(tplRequest)
.then(function(response) {
console.log('loaded, placing into cache');
var tpl = response.data;
$templateCache.put(templateName, tpl)
return tpl;
}
);
}],
controller: 'TableController'
},
}
And if we would not need headers, and we could cache, it is really very easy, as documented here:
Trying to Dynamically set a templateUrl in controller based on constant
Drafted version could be: (no custom headers but effective loading and caching)
templateProvider: ['$templateRequest', function(CONFIG, $templateRequest) {
var templateName = 'Generate/Table?objectId=WfObject';
return $templateRequest(templateName);
}],
templateUrl property can also take function as value. So you can add dynamic properties to the templateUrl via there.
templateUrl : function(stateParams) {
// before returning the URL, add additional properties and send
// stateParamsargument object refers to $stateParams and you can access any url params from there.
return 'Views/Tables';
}

AngularJS - $routeProvider and resolve not auto updating the view

I am having trouble getting resolve in $routeProvider to automatically update the view.
I am trying to convert an example (chapter 22) in Adam Freeman's Pro Angular book from Deployd to Express-Mongo. A possible hint is that the automatic refresh works using a local version of Deployd. When I switched to local Express and Mongo backend (after making the id to _id changes), the automatic view update no longer works.
When using Express-Mongo, I have to manually execute $route.reload() function to get show the updated changes from the database. I am new to angular so any hints are appreciated.
This heavy handed Delete works:
$scope.deleteProduct = function (product) {
product.$delete();
$route.reload();
$location.path("/list");
}
The below version Doesn't work. I was under the (probably mistaken) impression that the 'resolve' property in $routeProvider updates the view when the data is returned. With the below code, I get a Type Error: Undefined is not a function
$scope.deleteProduct = function (product) {
product.$delete().then(function () {
$scope.data.products.splice($scope.data.products.indexOf(product), 1);
});
$location.path("/list");
}
Console Log Errors
TypeError: undefined is not a function
at http://localhost:8080/bower_components/angular-resource/angular-resource.js:530:25
at Array.forEach (native)
at forEach (http://localhost:8080/bower_components/angular/angular.js:302:11)
at angular.module.factory.Resource.(anonymous function).$http.then.value.$resolved (http://localhost:8080/bower_components/angular-resource/angular-resource.js:529:17)
at deferred.promise.then.wrappedCallback (http://localhost:8080/bower_components/angular/angular.js:10905:81)
at deferred.promise.then.wrappedCallback (http://localhost:8080/bower_components/angular/angular.js:10905:81)
at http://localhost:8080/bower_components/angular/angular.js:10991:26
at Scope.$get.Scope.$eval (http://localhost:8080/bower_components/angular/angular.js:11906:28)
at Scope.$get.Scope.$digest (http://localhost:8080/bower_components/angular/angular.js:11734:31)
at Scope.ng.config.$provide.decorator.$delegate.__proto__.$digest (<anonymous>:844:31) angular.js:9383
Below is the code for the module
angular.module("exampleApp", ["ngResource", "ngRoute"])
.constant("baseUrl", "http://localhost:8080/api/products/")
.factory("productsResource", function ($resource, baseUrl) {
return $resource(baseUrl + ":id", { id: "#_id" },
{
create: { method: "POST" , isArray: true },
save: { method: "PUT", isArray: true },
delete: {method: "DELETE", isArray: true},
update: {method: "PUT", isArray: true},
query: {method: "GET", isArray: true}
})
})
.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
...
$routeProvider.otherwise({
templateUrl: "/views/tableView.html",
controller: "tableCtrl",
resolve: {
data: function (productsResource) {
return productsResource.query();
}
}
});
})
.controller("defaultCtrl", function ($scope, $location, productsResource, $route) {
$scope.data = {};
$scope.deleteProduct = function (product) {
product.$delete().then(function(products) {
//Success
console.log("Success"); //Don't Get Here
$scope.data.products.splice($scope.data.products.indexOf(product), 1);
}, function(errResponse) {
//Failure (Code Produces a Type Error: Undefined is not a function)
console.log("Error: " + errResponse);
});
$location.path("/list");
}
})
The Table View Controller that is bound to the $routeProvider.otherwise
.controller("tableCtrl", function ($scope, $location, $route, data) {
$scope.data.products = data;
$scope.refreshProducts = function () {
$route.reload();
} })
I think you should redirect when the delete is complete:
$scope.deleteProduct = function (product) {
product.$delete().then(function () {
$scope.data.products.splice($scope.data.products.indexOf(product), 1);
$location.path("/list");
});
}
You have missed } at the and of line with text:
delete: {method: "DELETE", isArray: true

Resources