unable to invoke uiCanExit - angular ui router 1.0.0 beta - javascript

I have the following routes file in components/app/app-routes.js:
export default
function AppRoutes($stateProvider, $urlRouterProvider, $transitionsProvider) {
'ngInject';
// reference: https://ui-router.github.io/guide/ng1/migrate-to-1_0#lazy-resolves
const defaultResolvePolicy = {
when: 'EAGER'
};
const STATES = [{
name: 'logout',
url: '/logout?applicationName&desktopName&sn',
}, {
name: 'base',
url: '',
abstract: true,
template: '<ui-view></ui-view>'
}, {
name: 'app',
parent: 'base',
abstract: true,
component: 'wireApp',
data: {
authRequired: true
},
resolvePolicy: defaultResolvePolicy,
resolve: {
labels(LabelService) {
'ngInject';
return LabelService.fetch();
},
settings(SettingsService) {
'ngInject';
return SettingsService.fetch();
},
}
}, {
name: '404',
url: '/404',
parent: 'base',
template: '<w-404></w-404>',
resolvePolicy: defaultResolvePolicy,
resolve: {
module($q, $ocLazyLoad) {
'ngInject';
return $q((resolve) => {
require.ensure([], (require) => {
let mod = require('pages/404');
$ocLazyLoad.load({
name: mod.name
});
resolve(mod.name);
}, '404');
});
},
}
}, {
name: 'dashboard',
parent: 'app',
url: '/dashboard',
data: {
authRequired: true
},
views: {
'content#app': {
template: '<w-dashboard priority-tasks="$resolve.priorityTasks"></w-dashboard>'
},
},
resolvePolicy: {
module: defaultResolvePolicy,
priorityTasks: {
when: 'LAZY'
},
},
resolve: {
priorityTasks($http, $q, CacheFactory, CustomerService, RuntimeConfig, PermissionService) {
'ngInject';
if (!CacheFactory.get('priorityTasks')) {
CacheFactory.createCache('priorityTasks', {
storageMode: 'sessionStorage',
storagePrefix: 'w'
});
}
const priorityTasksCache = CacheFactory.get('priorityTasks');
if (PermissionService.check('PRIORITY_TASKS', 'view')) {
return $http.get(`${RuntimeConfig.DEV_API_URL}/customer/${CustomerService.model.currentCustomer.id}/priority-tasks`, {
cache: priorityTasksCache
}).then(({
data
}) => data, () => $q.resolve([]));
}
return [];
},
module($q, $ocLazyLoad) {
'ngInject';
return $q((resolve) => {
require.ensure([], (require) => {
let mod = require('pages/dashboard');
$ocLazyLoad.load({
name: mod.name
});
resolve(mod.name);
}, 'dashboard');
});
}
}
}, {
name: 'loans',
parent: 'app',
url: '/loans',
data: {
authRequired: true
},
views: {
'content#app': {
template: '<w-loans></w-loans>',
},
},
resolvePolicy: defaultResolvePolicy,
resolve: {
security($q, $state) {
'ngInject';
//irl get this from a service.
console.log($transitionsProvider, "TRANSISIONS PROVIDER FROM ROUTE");
// let permissions = false;
// if (!permissions) {
// return $q.reject("No permissions");
// }
},
module($q, $ocLazyLoad) {
'ngInject';
return $q((resolve) => {
require.ensure([], (require) => {
let mod = require('pages/loans');
$ocLazyLoad.load({
name: mod.name
});
resolve(mod.name);
}, 'loans');
});
}
}
}];
$urlRouterProvider
.when('', '/dashboard')
.when('/', '/dashboard')
.when('/login', '/dashboard')
.otherwise('/404');
//this will redirect all rejected promises in routes to a 404.
$transitionsProvider.onError({
to: '*',
from: '*'
}, (transition) => {
let $state = transition.router.stateService;
$state.go('404');
});
STATES.forEach((state) => {
$stateProvider.state(state);
});
}
in my loans controller (associated state above, 'loans'), however, I am unable to access the new uiCanExit callback.:
.component('wLoans', {
template: require('./loans.html'),
controller: LoansController,
bindings: {
settings: '<',
labels: '<'
}
});
function LoansController($window, $timeout, $http, $compile, $log, $filter, LoansService, ConfigService, ngDialog, SettingsService, CustomerService, ColumnRenderService, $transitions) {
'ngInject';
this.uiCanExit = function () {
console.log("WHY AM I NOT GETTING HERE");
}
}
nothing appears in the console when I switch between states, and I'm trying to figure out what to do to get the uiCanExit lifecycle hook to be run when I switch in between states (particularly dashboard and loans)

I'm not sure about this, but could the problem be caused by not referencing the component directly? Probably this only works when you reference your loans component via the component key instead of placing them in a template which renders the component. I assume that in your case the router tries to find the callback in the (not declared and thus dummy) controller instead of using the component's controller.
Please have a look at the docs: https://ui-router.github.io/docs/latest/interfaces/ng1.ng1controller.html#uicanexit
You can validate this assumption by putting a controller implementation with the uiCanExit() method in your loans state.

Related

Angular UI Router - State params reset for parent when sharing controller in child state

I have ONE controller that is used by TWO child states (detail.Controller).
The parent states (QUIZ1 & QUIZ3) have different controllers that push different data to the parent scopes (quiz1.Controller & quiz3.Controller)
routes.js:
{
name: 'QUIZ1',
abstract: true,
url: '/quiz1',
templateUrl: 'app/html-partials/quiz.html',
controller: require('./app/controllers/quiz1.Controller').inject(angular.module('app', ['ui.router', require('angular-sanitize')]))
},
{
name: 'QUIZ1.detail',
url: '/:page',
templateUrl: 'app/html-partials/quiz.detail.html',
params: {
page: {
value: '0',
squash: true
},
score: {
value: '0',
squash: true
},
timer: {
value: false,
squash: true
}
},
controller: require('./app/controllers/detail.Controller').inject(angular.module('app', ['ui.router', require('angular-sanitize')]))
},
{
name: 'QUIZ2',
// some other state stuff
},
{
name: 'QUIZ3',
abstract: true,
url: '/quiz3',
templateUrl: 'app/html-partials/quiz.html',
controller: require('./app/controllers/quiz3.Controller').inject(angular.module('app', ['ui.router', require('angular-sanitize')]))
},
{
name: 'QUIZ3.detail',
url: '/:page',
templateUrl: 'app/html-partials/quiz.detail.html',
params: {
page: {
value: '0',
squash: true
},
score: {
value: '0',
squash: true
},
timer: {
value: false,
squash: true
}
},
controller: require('./app/controllers/detail.Controller').inject(angular.module('app', ['ui.router', require('angular-sanitize')]))
},
quiz1.Controller.js: (parent controller that builds data)
function buildData(resp) {
$scope.slides = [];
for (var item in resp.quiz_slides) {
if (resp.quiz_slides) {
$scope.slides.push(resp.quiz_slides[item]);
}
}
}
detail.Controller.js: (partial where breakpoint is erroring):
$scope.slide = $scope.slides[$stateParams.page];
THE PROBLEM:
When i navigate out of QUIZ1.detail with a $state.go('QUIZ2') and eventually into a page of QUIZ3.detail I get:
angular.js:14328 TypeError: Cannot read property '0' of undefined
This is relating to the $stateParams.page (used for navigation) I think as it's not correctly resetting because the old data might still be in the $scope.
I KNOW THIS because navigating IMMEDIATELY to QUIZ3 will parse the data and no errors.
Has anyone had any experience with this?
Ok, if any one is wondering how I got around this, after putting it here and there and airing it to colleagues, I thought: "$rootScope for the slides"!
NOTE: naming conventions differ slightly from answer, but you get the idea.
I introduced an init state in between the states so that it would properly parse the json stuff and throw the state into a new child called .detail:
init-state.js:
exports.inject = function (app) {
app.controller('sevenIntAFFECTinit.Controller', exports.controller);
exports.controller.$inject = ['$scope', '$state', '$stateParams', 'jsonPartialService', '$log', '$rootScope'];
return exports.controller;
};
exports.controller = function sevenIntAFFECTinitCtrl($scope, $state, $stateParams, jsonPartialService, $log, $rootScope) {
// Do the json in here. Jokes
$rootScope.slides = [];
jsonPartialService
.getJSON('part-1/7-INT-QUIZ-AFFECT')
.then(function (response) {
$scope.prevState = response.prev_state;
$scope.nextState = response.next_state;
$scope.quizTitle = response.quiz_title;
buildData(response);
});
function buildData(resp) {
for (var item in resp.quiz_slides) {
if (resp.quiz_slides) {
$rootScope.slides.push(resp.quiz_slides[item]);
}
}
$log.debug('after the slides have been pushed in init -->', $rootScope.slides);
$state.go('7-INT-QUIZ-AFFECT.detail', $stateParams, {reload: true, inherit: false});
}
};

Access a service in AngularUI Router onEnter callback

Im using Angular 1.5.6 and am using AngularUI Router (https://github.com/angular-ui/ui-router). I have different routes e.g. customer and users. In each of these there are different 'sub-roots' e.g. one for list and one for edt. Im setting up the customer route here:
import customerListModule from './list/customer.list';
import customerServiceModule from './services/customer.service';
...
...
function customerModule($stateProvider, $urlRouterProvider) {
'ngInject';
$urlRouterProvider
.when('/customer', ['$state', function($state) {
$state.go('customer.list.tracked');
}]);
$stateProvider
.state('customer', {
parent: 'root',
url: '/customer',
abstract: true,
views: {
'root#app': {
template: '<div class="customer" ui-view=""></div>'
}
},
onEnter: () => {
// in here I want to change my customer servce
},
})
.state('customer.list', {
url: '',
views: {
'#customer': {
template: '<customer></customer>'
}
},
breadcrumbs: {
name: 'customer.breadcrumbs.list'
},
params: {
saving: false
}
})
.state('customer.edit', {
parent: 'customer.list',
url: '/:id/edit',
views: {
'#customer': {
template: editTemplate(),
controller: manageCustomerCtrl,
controllerAs: 'manageCustomerVM'
}
},
breadcrumbs: {
name: 'customer.breadcrumbs.edit'
},
resolve: {
isAuthorized: 'readWriteAccess'
},
bodyClass: 'product-form'
});
}
export default angular.module('customerAdminUI.customer', [
'ui.bootstrap',
'ui.router',
customerListModule.name,
customerServiceModule.name,
...
...
])
.config(customerModule);
I have a customer service which I want to access in the onEnter callback of the customer state. I tried to inject it into the customerModule method so that I can use it in the onEnter() callback:
function customerModule($stateProvider, $urlRouterProvider, CustomerService) {
...
...
$stateProvider
.state('customer', {
parent: 'root',
url: '/customer',
abstract: true,
views: {
'root#app': {
template: '<div class="customer" ui-view=""></div>'
}
},
onEnter: () => {
CustomerService.clearSearch();
},
})
However I get the error:
Unknown provider: CustomerService
How can I use a service in the onEnter callback?
We can ask for a service as a param
// not ready for minification
onEnter: function(CustomerService) {
CustomerService.dowhatneeded...
},
// preferred way
onEnter: ['CustomerService', function(service) {
service.dowhatneeded...
}],

Issue on access a service in a ui-router controller

I am having issues trying to access a service on a controller. The issue happen when the Ordenes services is called. How I can call a service with two parameter using values from the scope from a controller using ui-router?
I have the same code working but without the use of ui-router. It seems like the code is not loading properly the service inside the Controller.
App.js
'use strict';
app = angular.module('logica-erp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router',
'authorization',
'ui.router.stateHelper',
'logica-erp.kds',
'logica-erp.pos'
])
app.run(function($rootScope) {
$rootScope.$on("$stateChangeError", console.log.bind(console));
});
app.config(function ($stateProvider, $urlRouterProvider) {
//delete $httpProvider.defaults.headers.common['X-Requested-With'];
$urlRouterProvider.otherwise('/');
$stateProvider
.state('index', {
url: '/',
templateUrl: 'views/main.html',
controller:'MainCtrl'
})
.state('comanda', {
url: '/comanda',
templateUrl: 'views/comanda.html',
controller:'ComandaCtrl'
})
.state('counter', {
url: '/counter',
templateUrl: 'views/counter.html',
controller:'CounterCtrl'
})
})
comanda.js
(function() {
'use strict';
var app;
app = angular.module('logica-erp.kds', ['timer', 'logica-erp.service.pos']);
this.ComandaCtrl = [
'$scope', '$interval', 'Ordenes', function($scope, $interval, Ordenes) {
var error, stop, success, tick;
$scope.tiempos = [
{
name: '15 seg',
value: 15000
}, {
name: '30 seg',
value: 30000
}, {
name: '60 seg',
value: 60000
}, {
name: '120 seg',
value: 120000
}
];
$scope.selected_tiempo = $scope.tiempos[1];
$scope.tipos = [
{
name: 'Alimentos',
value: 'a'
}, {
name: 'Bebidas',
value: 'b'
}, {
name: 'Todos',
value: ''
}
];
$scope.selected_tipo = $scope.tipos[2];
success = function(result) {
if (angular.toJson(result) !== angular.toJson($scope.ordenes)) {
$scope.isLoading = true;
$scope.ordenes = result;
console.log(JSON.stringify($scope.ordenes));
}
return $scope.isLoading = false;
};
error = function(error) {
console.log('error ' + error);
return $('#modal').foundation('open');
};
tick = function() {
$scope.platos = Ordenes.query({
tipo: $scope.selected_tipo.value,
sucursal: 2
});
return $scope.platos.$promise.then(success, error);
};
tick();
stop = $interval(tick, $scope.selected_tiempo.value);
$scope.change_refresh = function() {
$interval.cancel(stop);
return stop = $interval(tick, $scope.selected_tiempo.value);
};
return $scope.update_order = function(mesa, aaybb_id) {
return angular.forEach($scope.ordenes.mesas, function(orden) {
if (orden.mesa === mesa) {
return angular.forEach(orden.aaybb, function(aaybb) {
if (aaybb._id === aaybb_id) {
if (aaybb.estatus === 'ASIGNADO') {
aaybb.estatus = 'EN PROCESO';
} else if (aaybb.estatus === 'EN PROCESO') {
aaybb.estatus = 'PREPARADO';
$('#timer_' + aaybb._id)[0].stop();
}
return Ordenes.update(aaybb);
}
});
}
});
};
}
];
app.controller('ComandaCtrl', ComandaCtrl);
}).call(this);
Console log
Error: value is undefined
extractParams/<#http://127.0.0.1:9000/bower_components/angular-resource/angular-resource.js:344:11
forEach#http://127.0.0.1:9000/bower_components/angular/angular.js:336:11
extractParams#http://127.0.0.1:9000/bower_components/angular-resource/angular-resource.js:343:9
ResourceFactory/</Resource[name]#http://127.0.0.1:9000/bower_components/angular-resource/angular-resource.js:398:39
this.ComandaCtrl</tick#http://127.0.0.1:9000/scripts/controllers/comanda.js:72:25
this.ComandaCtrl<#http://127.0.0.1:9000/scripts/controllers/comanda.js:78:7
I fixed the issue, it was a old bug in the angular-resource lib. I didn't know but my bower was installing version 1.0.7 :S anyway; this was very annoying.

How can I lazy configure types of angular formly?

I'm trying to lazy configure custom types of angular-formly with ocLazyLoad but I cannot. When the state is executing I'm trying to call the setType function but the page does not load anything after that. When I remove the setType function everything works fine. Is there any way to lazy configure the formly types?
formlyConfig.setType({
name: 'input',
template: '<input ng-model="model[options.key]" />'
});
Roughly, this is the example:
//ocLazyLoad Configurations
$ocLazyLoadProvider.config({
events: true,
debug: true,
modules: [
{
name: "formly",
files: [ "Scripts/formly/angular-formly.js" ]
},
{
name: "formlyBootstrap",
files: [ "Scripts/formly/angular-formly-templates-bootstrap.js" ]
}
]
});
//Ui-Router Configs
$stateProvider
.state("admin", {
abstract: true,
url: "/admin",
templateUrl: "App/admin/templates/content.html",
resolve: {
loadApiCheck: ["$ocLazyLoad", function ($ocLazyLoad) {
return $ocLazyLoad.load("Scripts/formly/api-check.js");
}],
loadFormly: ["loadApiCheck", "$ocLazyLoad", function (loadApiCheck, $ocLazyLoad) {
return $ocLazyLoad.load("formly");
}],
loadFormlyBootstrap: ["loadFormly", "formlyConfig", "$ocLazyLoad", function (loadFormly, formlyConfig, $ocLazyLoad) {
//* * *
//After formlyConfig.setType() nothing gets executed
//* * *
debugger;
formlyConfig.setType({
name: 'input',
template: '<input ng-model="model[options.key]" />'
});
return $ocLazyLoad.load("formlyBootstrap");
}]
}
})
.state("admin.contact", {
url: "/contact",
controller: "contactCtrl",
controllerAs: "vm",
templateUrl: "App/admin/templates/contact.html",
resolve: {
loadFunctionalityFiles: ["$ocLazyLoad", function ($ocLazyLoad) {
return $ocLazyLoad.load({
serie: true,
files: [
"App/admin/factories/userFactory.js",
"App/admin/controllers/contactController.js"
]
});
}]
}
});
Finally, here is the documentation, just in case: Angular-Formly extending types
And here i've made a plnkr test case
#kentcdodds i managed to solve this particular problem i think but i'm not 100% sure, however in this example it is working. The solution to my problem is to call setType function after formlyBootstrap is loaded.
The code to achieve that is the following:
$stateProvider
.state("admin", {
abstract: true,
url: "/admin",
templateUrl: "content.html",
resolve: {
loadApiCheck: ["$ocLazyLoad", function($ocLazyLoad) {
return $ocLazyLoad.load("//npmcdn.com/api-check#latest/dist/api-check.js");
}],
loadFormly: ["loadApiCheck", "$ocLazyLoad", function(loadApiCheck, $ocLazyLoad) {
return $ocLazyLoad.load("formly");
}],
loadFormlyBootstrap: ["loadFormly", "$ocLazyLoad", "formlyConfig", function(loadFormly, $ocLazyLoad, formlyConfig) {
return $ocLazyLoad
.load("formlyBootstrap")
.then(function() {
console.log("reached critical point...");
formlyConfigurations(formlyConfig);
console.log("passed critical point!");
});
}]
},
onEnter: ["$log", "$state", function($log, $state) {
$log.info("state admin entered");
}]
});
here is the plnkr

angularjs $routeProvider route is executed before resolve completes

I would like the route.resolve method(s) to fire before the actual route code is run. Unfortunately in the code below, prime() gets called but it is called asynchronously and the route code gets called before the prime completes. I thought the resolve methods of a route was suppose to complete before the route is loaded?
(function () {
'use strict';
var app = angular.module('app');
// Collect the routes
app.constant('routes', getRoutes());
// Configure the routes and route resolvers
app.config(['$routeProvider', 'routes', routeConfigurator]);
function routeConfigurator($routeProvider, routes) {
routes.forEach(function (r) {
setRoute(r.url, r.config)
});
$routeProvider.otherwise({ redirectTo: '/' });
function setRoute(url, definition) {
//set resolvers for all of the routes
//by extending any existing resolvers (or creating a new one)
definition.resolve = angular.extend(definition.resolve || {}, {
prime: prime
});
$routeProvider.when(url, definition);
return $routeProvider;
}
}
prime.$inject = ['datacontext'];
function prime(dc) {
dc.prime();
}
// Define the routes
function getRoutes() {
return [
{
url: '/',
config: {
templateUrl: 'app/dashboard/dashboard.html',
title: 'dashboard',
settings: {
nav: 1,
content: '<i class="icon-dashboard"></i> Dashboard'
}
}
},
{
url: '/sessions',
config: {
title: 'admin',
templateUrl: 'app/sessions/sessions.html',
settings: {
nav: 2,
content: '<i class="icon-calendar"></i> Sessions'
}
}
},
{
url: '/speakers',
config: {
title: 'speakers',
templateUrl: 'app/speakers/speakers.html',
settings: {
nav: 3,
content: '<i class="icon-user"></i> Speakers'
}
}
},
{
url: '/attendees',
config: {
title: 'attendees',
templateUrl: 'app/attendees/attendees.html',
settings: {
nav: 4,
content: '<i class="icon-group"></i> Attendees'
}
}
}
];
}
})();
Try changing prime to the following:
function prime(dc) {
return dc.prime();
}
I suggest you re-position the prime function to the global controller defining it as:
$scope.prime = function (dc) {
dc.prime();
};
Move prime inside scope of routeConfigurator
(function () {
'use strict';
var app = angular.module('app');
// Collect the routes
app.constant('routes', getRoutes());
// Configure the routes and route resolvers
app.config(['$routeProvider', 'routes', routeConfigurator]);
function routeConfigurator($routeProvider, routes) {
routes.forEach(function (r) {
setRoute(r.url, r.config);
});
$routeProvider.otherwise({ redirectTo: '/' });
function setRoute(url, definition) {
definition.resolve = angular.extend(definition.resolve || {}, { prime: prime });
$routeProvider.when(url, definition);
return $routeProvider;
}
prime.$inject = ['datacontext'];
function prime(datacontext) {
return datacontext.prime();
}
}
// Define the routes
function getRoutes() {
return [
{
url: '/',
config: {
templateUrl: 'app/dashboard/dashboard.html',
title: 'dashboard',
settings: {
nav: 1,
content: '<i class="fa fa-dashboard"></i> Dashboard'
}
}
},
{
url: '/sessions',
config: {
title: 'sessions',
templateUrl: 'app/sessions/sessions.html',
settings: {
nav: 2,
content: '<i class="fa fa-calendar"></i> Sessions'
}
}
},
{
url: '/speakers',
config: {
title: 'speakers',
templateUrl: 'app/speakers/speakers.html',
settings: {
nav: 3,
content: '<i class="fa fa-user"></i> Speakers'
}
}
},
{
url: '/attendees',
config: {
title: 'attendees',
templateUrl: 'app/attendees/attendees.html',
settings: {
nav: 4,
content: '<i class="fa fa-group"></i> Attendees'
}
}
}
];
}
})();

Categories