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...
}],
Related
I'm working on a web-app with ionic tabs.
On one of my pages i implemented the ion-tree-list from GitHub (Ion-tree-list from GitHub) as treeview.
In services.js i got the source for my tree:
.factory('VerificationTree', function () {
var verificationTree = [
{
id: 'operator',
name: 'Operator'
},
{
id: 'type_of_test',
name: 'Type of test',
},
{
id: 'system',
name: 'System'
},
{
id: 'component',
name: 'Component'
},
{
id: 'component_group',
name: 'Component group',
tree: [
{
id: 'component2',
name: 'Component'
}
]
}
];
return {
all: function () {
return verificationTree;
}
};
})
When i click on one the tree items - p.e. "type_of_test" - i want to open another page.
In controller.js i have defined a controller for the page with the tree.
Inside the Controller is a function, which opens the page by window.open.
.controller('VerificationCtrl', function ($scope, VerificationTree) {
$scope.verificationTree = VerificationTree.all();
$scope.$on('$ionTreeList:ItemClicked', function (event, item) {
if (item.id == 'type_of_test') {
window.open('templates/type-of-test.html');
};
//if (item.id == 'component2') {
// alert('The ion-tree-list item component2 was clicked');
//};
})
})
The page opens in a new tab but the layout is not okay anymore.
Is there a possibility to combine the tree with a navigation view like the following?
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
});
Or another example:
var app = angular.module('myApp', ['ionic']);
app.config(function($stateProvider) {
$stateProvider
.state('index', {
url: '/',
templateUrl: 'home.html'
})
.state('music', {
url: '/music',
templateUrl: 'music.html'
});
});
Not sure if I get you right, but what about doing a simple state change, when an item is clicked?
$stateProvider.state("tabs", {
url: "/tabs",
templateUrl: "templates/TabView.html",
abstract: true,
controller: "TabCtrl as tab"
}).state("tabs.home", {
url: "/overview",
templateUrl: "templates/HomeView.html",
controller: "HomeCtrl"
}).state("tabs.detail", {
url: "/detail/:id",
templateUrl: "templates/DetailView.html",
controller: "DetailCtrl"
})
On HomeView, add your TreeList and define the click function as follows:
.controller('HomeCtrl', function ($scope, $state, VerificationTree) {
$scope.verificationTree = VerificationTree.all();
$scope.$on('$ionTreeList:ItemClicked', function (event, item) {
if (item.id == 'type_of_test') {
$state.go('tabs.detail')
};
})
})
If you want to pass in the item as a param, you can define this in the stateProvider configuration as well. Check the UI-Router docs for additional information. https://github.com/angular-ui/ui-router/wiki/URL-Routing
Also if you don't want to transition to another tab when clicking an item, but stay in the tab and just change the state, you might try nested states. You could define your states like this:
$stateProvider.state("tabs", {
url: "/tabs",
templateUrl: "templates/TabView.html",
abstract: true,
controller: "TabCtrl as tab"
}).state("tabs.home", {
url: "/home",
abstract: true,
views: {
"home": {
template: "<ion-nav-view></ion-nav-view>"
}
}
}).state("tabs.home.overview", {
url: "/overview",
templateUrl: "templates/HomeView.html",
controller: "HomeCtrl"
}).state("tabs.home.detail", {
url: "/detail/:id",
templateUrl: "templates/DetailView.html",
controller: "DetailCtrl"
})
And then transition from Overview to Detail like this:
$state.go('tabs.home.detail')
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.
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
I have the following state configuration. How can I get id parameters in index state # view so that it could be accessible by both, left and detail views controllers? Now if I try to access it via $state.params.id it is undefined, but if I console.log($state) I can see the parameter in my console. I only need the parameter if available on first load of left view.
$stateProvider
.state('index', {
url: '/',
views: {
'#' : {
template: '/**/'
resolve: {
params: function ($state) {
return $state.params.id;
}
}
},
'left#index' : {
template: '/**/',
controller: 'AppController',
controllerAs: 'app'
},
'main#index' : {
template: '/**/'
}
})
.state('index.detail', {
url: '/:id',
views: {
'detail#index' : {
template: '/**/',
controller: 'DetailController',
controllerAs: 'detail',
}
}
});
I have now tried whole day and i cant figure out how i shall solve this issue.
My goal is to display a sidebar thats get filled with data based on what state it is.
Lets say i am on the home page i may like this items:
Home item 1
Home item 2
And if i am on the about page i want this items:
About me
About my dog
I would like the data to get fetched from a service that returns data to the view based on what state it is.
I have tried to use ui.router's resolve function but i can't get the correct structure in my head to make it work.
Created a plunkr that shows how i mean but without the solution, what are the best practices and preferred ways when creating a dynamic sidebar with Angular and ui.router?
myapp.config(function($stateProvider) {
$stateProvider
.state('home', {
url: "",
views: {
"content": {
template: "This is the home.content"
},
"sidebar": {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl'
}
}
})
.state('about', {
url: "/about",
views: {
"content": {
template: "This is the about.content"
},
"sidebar": {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl'
}
}
})
})
Plunkr here
Edit
Can't figure out what i am doing wrong, no view is shown with this code:
app.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to root
$urlRouterProvider.otherwise("/");
$stateProvider
.state('root', {
url: '',
'abstract': true,
views: {
'sidebar#': {
templateUrl: '/App/Main/views/shared/sidebars/sidebar.cshtml',
controller: 'app.controllers.views.shared.sidebars.sidebar',
resolve: {
sidebarData: function (sidebarService) {
return sidebarService.getActions();
}
}
}
},
})
.state('root.home', {
url: "/",
views: {
'': {
templateUrl: '/App/Main/views/home/home.cshtml',
controller: 'app.controllers.views.home'
},
'content#root.home': {
templateUrl: '/App/Main/views/home/home-content.cshtml',
controller: 'app.controllers.views.home'
}
}
})
.state('root.about', {
url: "/customers",
views: {
'': {
templateUrl: '/App/Main/views/customers/customers.cshtml',
controller: 'app.controllers.views.customers'
},
'content#root.about': {
templateUrl: '/App/Main/views/customers/customers-content.cshtml',
controller: 'app.controllers.views.customers'
}
}
});
}]);
and here is how my home and customer views look like:
<div data-ui-view="sidebar">
Loading sidebar view.
</div>
<div data-ui-view="content">
Loading content view.
</div>
you could try to implement named and also an abstract view for your sidebar to reuse it among your other routes, also you can use the resolve parameter an return whatever dynamic data you need (from a service, resource, whatever), it could be something like this:
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider) {
$stateProvider
.state('root',{
url: '',
abstract: true,
views: {
'sidebar#': {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl',
resolve:{
sidebarData:function(){
return [{
state: '/stateHere',
text: 'Need'
}, {
state: '/stateHere',
text: 'to'
}, {
state: '/stateHere',
text: 'fill'
}, {
state: '/stateHere',
text: 'this'
}, {
state: '/stateHere',
text: 'with'
}, {
state: '/stateHere',
text: 'dynamic'
}, {
state: '/stateHere',
text: 'data'
}];
}
}
}
})
.state('root.home', {
url: "/",
views: {
'content#': {
template: "This is the home.content"
}
}
})
.state('root.about', {
url: "/about",
views: {
'content#': {
template: "This is the about.content"
}
}
})
});
myapp.controller('sidebarCtrl', ['$scope','sidebarData'
function($scope,sidebarData) {
$scope.sidebarData= sidebarData,
}
]);
EDIT:
check this working example:
http://plnkr.co/edit/Nqwlkq1vGh5VTBid4sMv?p=preview