I am working on an application and I'd like to use the controllerAs syntax to not rely only on $scope. I am using $resource to get data from the API and the problem I encounter is that in the success/error callbacks I can use only $scope, since this is not defined.
Here is some code to better explain the problem.
This is my main module where among other things I configure the router:
angular
.module('app', ['ngRoute', 'ngResource', 'LocalStorageModule', 'app.users', 'app.auth'])
.config(configure)
.controller('MainController', ['$scope', '$location', MainController]);
function configure($routeProvider, localStorageServiceProvider, $resourceProvider) {
// configure the router
$routeProvider
.when('/', {
templateUrl: 'app/homepage.html',
controller: 'MainController',
controllerAs: 'vm',
data: { authRequired: true }
})
.when('/users', {
templateUrl: 'app/users/main.html',
controller: 'UserController',
controllerAs: 'vmu',
data: { authRequired: true }
})
.otherwise({redirectTo: '/'});
}
// the MainController is not relevant here
In the user module I get some info about the users from the API. Here is a simplified example:
angular
.module('app.users', ['ngResource'])
.controller('UserController', ['UserService', UserController])
.factory('UserService', ['$resource', UserService]);
function UserController(UserService) {
this.users = UserService.users.list();
this.getUserInfo = function(userId) {
this.user = UserService.users.single({ id: userId },
function(success) {
// here I'd like to use 'this' but the following line will trigger an error
this.groupRules = UserService.users.rules({ id: success.userGroupId });
// I have to use $scope instead but it is not what I want
// $scope.groupRules = UserService.users.rules({ id: success.userGroupId });
} );
}
}
function UserService($resource) {
var userResource = {};
userResource.users = $resource('https://my.api.com/users/:action',
{},
{
list: { method: 'GET', isArray: true, params: { action: 'list' } }
single: { method: 'GET', params: { action: 'single', id: '#id' } }
rules: { method: 'GET', params: { action: 'getRules', id: '#id' } }
});
return userResource;
}
I'd like to be able to use 'this' in the callback of the $resource, but of course I'll get an error since 'this' is 'undefined' inside the callback.
Using $scope solves the problem, but I need to refactor some code and I'd like to avoid using $scope all the time.
Any workaround? Maybe I should use a different approach?
Thanks in advance for your help and explanations!
You should look into how to use this in javascript and into javascript scopes and closures.
This should work better:
function UserController(UserService) {
var _this = this;
this.users = UserService.users.list();
this.getUserInfo = function(userId) {
_this.user = UserService.users.single({ id: userId },
function(success) {
// here I'd like to use 'this' but the following line will trigger an error
_this.groupRules = UserService.users.rules({ id: success.userGroupId });
} );
}
}
Related
I have a small question regarding scopes and promises.
I declared wiz_ids outside a promise call and I would like to access it again when the promise is resolved.
I tried to use bind() but without luck.
This is my state:
state: 'wizard',
config: {
url: '/wizard',
templateUrl: 'app/partners/wizard/wizard.html',
controller: 'WizardController',
controllerAs: 'vm',
redirectTo: 'wizard.step1',
resolve: {
/* #ngInject */
promiseData: ['$window', '$location', 'WizardDataService', function ($window, $location, WizardDataService) {
var wiz_ids = {
'wiz_install_id': $location.search().install_id,
'wiz_instance_id': $location.search().instance_id
};
return WizardDataService.getWizardData(wiz_ids)
.then(function (response) {
// How do I access wiz_ids from here? //
return response.data;
});
}]
},
}
You could return a more complex object inside then().
Something like:
return WizardDataService.getWizardData(wiz_ids)
.then(function(response) {
var data = {
wiz_ids: wiz_ids,
wiz_data: response.data
}
return data;
});
Then in controller access the individual properties accordingly
When i bundle the js files then resolve statement is not working in angular project. Below is the code
app.js
app.config(["$stateProvider",
"$urlRouterProvider",
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("home", {
// welcome
url: "/",
templateUrl: "app/views/welcomeView.html"
})
.state("productList", {
// Product List
url: "/products",
templateUrl: "app/views/productListView.html",
controller: "ProductListCtrl as vm"
})
.state("productDetail", {
// Product Details
url: "/products/:productId",
templateUrl: "app/views/productDetailView.html",
controller: "ProductDetailCtrl as vm",
resolve: {
productResource: "productResource",
product: function (productResource, $stateParams) {
var productId = $stateParams.productId;
return productResource.get({ id: productId }, function (data) {
return data;
});
}
}
});
}]
);
productDetailCtrl.js
(function () {
"use strict";
angular
.module("productManagement")
.controller("ProductDetailCtrl",["product",ProductDetailCtrl]
);
function ProductDetailCtrl(product) {
var vm = this;
vm.product = product;
vm.title = "Product Detail: " + vm.product.productName;
}
}());
After login i can see product list perfectly but when I try to see details of product nothing happens not error in console as well. If I comment resolve property and remove product parameter in controller then it works fine. This problem occur only after bundling and minification. If I refer bare files its working fine. Please suggest me where I made mistake.
You can use the same pattern of passing an array with the argument names as strings before the function anywhere angular uses dependency injection.
resolve: {
productResource: "productResource",
product: ["productResource", "$stateParams", function (productResource, $stateParams) {
...
}]
}
Use in-line array annotation for the resolver function:
resolve: {
productResource: "productResource",
//product: function (productResource, $stateParams) {
product: ["productResource", "$stateParams", function (productResource, $stateParams) {
var productId = $stateParams.productId;
return productResource.get({ id: productId }, function (data) {
return data;
}).$promise;
//}
}]
}
For more information, see AngularJS Developer Guide - Dependency Injection -- Inline Array Annotation
I have an app that uses angular. In the app.config is where I have to setup my routes, however I want to authorize my routes because not everyone who uses the app can see every page.
So I have already tried to make a factory that gives a boolean to tell the app if that person can see the route or not, and learned that I cannot inject a factory into a config.
So I have made a provider that I can inject into the config:
(function () {
'use strict';
angular.module('services')
.factory('Auth', ['$http', function AuthFactory($http) {
return {
LinkAuth: function (Name) {
return $http({ method: 'GET', url: '/Dashboard/AuthorizeNavItem', data: { Name: Name } });
}
}
}]);
angular.module('services')
.provider('AuthProvider', ['Auth', function (Auth) {
var allowed = false;
this.$get = function (Name) {
Auth.LinkAuth(Name).success(function (data) {
allowed = data.Authorized;
});
return allowed;
}
}]);
})();
My app.config:
(function () {
'use strict';
angular.module('app.it', [])
.config(['$stateProvider', 'msNavigationServiceProvider', 'AuthProvider', function ($stateProvider, msNavigationServiceProvider, AuthProvider) {
$stateProvider
.state('app.it', {
abstract: true,
url: '/information-technology',
})
.state('app.it.users', {
url: '/users',
views: {
'content#app': {
templateUrl: '/IT/Users',
controller: 'ITUserController as vm'
}
}
});
/*
We need to get a bool to say if a user is part of a group that can see this page.
*/
var allowed = true;//I want the provider $get method to return that bool here
if (allowed)
{
//This builds the navigation bar
msNavigationServiceProvider.saveItem('app.authroute', {
title: 'Authorized Route',
icon: 'icon-monitor',
weight: 2
});
//This builds the navigation bar
msNavigationServiceProvider.saveItem('app.authroute.route', {
title: 'Route',
state: 'app.authroute.route'
});
}
}]);
})();
How do I access that AuthProvider $get and store the bool in a variable in the config?
First, thanks for your help in advance. I'm a noob to angular though I did search quite a bit trying to resolve this issue. I'm guessing I missed something obvious. I'm using ui-router and using the resolve property to call a resource and can't get it for the life of me to pass the params to the web service.
First, my routes and states:
(function () {
"use strict";
var app = angular.module("MyModule",
["ui.router",
"ngResource",
"common.services"]);
app.config(["$stateProvider", '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider.state("home", {
url: "/",
templateUrl: "welcomeView.html"
})
$stateProvider.state("vehicleList", {
url: "/vehicles",
templateUrl: "App/Vehicles/vehicleListView.html",
controller: "VehicleListController as vm"
})
$stateProvider.state("vehicleEdit", {
url: "/vehicles/edit/:vin",
templateUrl: "App/Vehicles/vehicleEditView.html",
controller: "VehicleEditController as vm",
resolve: {
vehicleResource: "vehicleResource",
vehicle: function (vehicleResource, $stateParams) {
var vehicleVIN = $stateParams.vin;
return vehicleResource.get(
{ vin: $stateParams.vin }).$promise;
}
}
})
$stateProvider.state("vehicleDetails", {
url: "/vehicles/:vin",
templateUrl: "App/Vehicles/vehicleDetailsView.html",
controller: "VehicleDetailsController as vm",
resolve: {
vehicleResource: "vehicleResource",
vehicle: function (vehicleResource, $stateParams) {
var vehicleVIN = $stateParams.vin;
return vehicleResource.get(
{ vin: 'T123432342' }).$promise;
}
}
})
}
]);
}());
Note that you see a couple of varieties of passing the vin is as I've tried numerous ways with passing in the variable vehicleVIN, a string, etc. The vehicleVIN variable does assign properly there so injecting $stateParams or passing the vin to it doesn't seem to be the problem.
Here is the resource:
(function () {
"use strict";
angular
.module("common.services")
.factory("vehicleResource",
["$resource",
"appSettings",
vehicleResource]);
function vehicleResource($resource, appSettings) {
return $resource(appSettings.serverPath + "/api/Vehicle/:vin", { vin: '#vin' },
{
'update': { method: 'PUT' },
'details': { method: 'GET' },
'query': { method: 'GET', isArray = true }
});
}
}());
Here is common.services:
(function () {
"use strict";
angular
.module("common.services", ["ngResource"])
.constant("appSettings",
{
serverPath: "http://localhost:8098"
});
}());
And here is the service end of it (Asp.Net WebAPI):
// GET api/Vehicle
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All, AllowedOrderByProperties = "Name")]
[ResponseType(typeof(IQueryable<IEnumerable<IVehicle>>))]
public IHttpActionResult Get()
{
return ListOfVehicles...blah..blah
}
// GET api/Vehicle/5
[ResponseType(typeof(IQueryable<IVehicle>))]
public IHttpActionResult Get(string vin)
{
return SingleVehicle...blah...blah
}
Every time I execute it, the breakpoint hits Get (without the vin parameter) and when I run fiddler, it looks like it's sending an empty body.
Any ideas on what is going on? Much appreciated!
This works, but Json is not how I want to retrieve the data:
var states = [
{ name: 'main', url: '/', templateUrl: '/views/main.html', controller: 'MainCtrl' },
{ name: 'login', url: '', templateUrl: '/views/login-form.html', controller: 'LoginCtrl' },
{ name: 'logout', url: '', templateUrl: '', controller: 'LogoutCtrl' },
{
name: 'sales',
url: '',
templateUrl: '/views/sales-data.html',
controller: 'SalesDataCtrl',
resolve: {
user: 'User',
authenticationRequired:
['user', function(user) { user.isAuthenticated(); }]
}
}
];
angular.forEach(states, function (state) {
$stateProvider.state(state.name, state);
});
This does not work in retrieving the data:
app.config(['$locationProvider', '$resourceProvider', '$stateProvider', function ($locationProvider, $resourceProvider, $stateProvider) {
$locationProvider.html5Mode(true);
not working ---> var states = $resource('http://localhost:9669/api/breeze/states').query();
angular.forEach(states, function(state) {
$stateProvider.state(state.name, state);
});
}]);
My question is two-fold:
How does one retrieve remote data inside app.config so as to populate $StateProvider.
I know even when that is acheived, I will have a problem with the return value of
"['user', function(user) { user.isAuthenticated(); }]"
as it will come back as a string and angular will not recognize it as a function.
How can I overcome that issue?
[Side note: the api call does work, the issue is not the api controller.]
Thank you.
[Solution]
Documentation is piece-meal on this matter, but after sewing various posts together I found the answer.
First one must understand that data cannot be populated in the $stateProvider as it is part of app.config because app.config only initializes providers, the actual http service has to be performed in app.run.
Step 1: Declare 2 Globals, ie. $stateProviderRef and $urlRouterProviderRef references before the app.module and pass the actual $stateProvider & $urlRouterProvider references to them in app.config:
(function() {
var id = 'app';
var $stateProviderRef = null;
var $urlRouterProviderRef = null;
......
// Create the module and define its dependencies.
app.config(function($stateProvider, $urlRouterProvider) {
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
});
.......
Step 2: Create a Custom Factory Provider:
app.factory('menuItems', function($http) {
return {
all: function() {
return $http({
url: 'http://localhost:XXXX/api/menuitems',
method: 'GET'
});
}
};
});
Step 3: Call the custom provider in app.run:
app.run(['$q', '$rootScope', '$state','$urlRouter', 'menuItems',
function($q, $rootScope, $state, $urlRouter, menuItems) {
breeze.core.extendQ($rootScope, $q);
menuItems.all().success(function (data) {
angular.forEach(data, function (value, key) {
var stateName = 'state_' + value.OrderNum; //<-- custom to me as I have over 300 menu items and did not want to create a stateName field
$stateProviderRef.state(stateName, {url:value.url, templateUrl:value.templateUrl, controller:value.controller });
});
// because $stateProvider does not have a default otherwise
$urlRouterProviderRef.otherwise('/');
});
.....
}
]);