I am using angular-ui-router to make my app organized by routing but I am little confused. as I know I have to add 'ui-router' dependencie in module to use $stateProvider. but even though I do not add 'ui-router' dependencie, It is working well.
I know angular service is singleton object so if I add 'ui-router' dependencie only once. can I use $stateProvider service in any module?
below code is angular code defined in my app.
angular.module('app', ['ui-router','app.pages']);
angular.module('app.pages', ['app.pages.core']);
angular.module('app.pages.core',[])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('common.default', {
abstract: true,
views: {
header: {
templateUrl: '/public/app/common/components/header/header.html',
controller: 'headerController'
},
content: {
template: '<div ui-view></div>'
}
}
})
.state('common.default.core', {
url: "/goongles/",
templateUrl: "/public/app/pages/core.html",
controller: 'coreController'
});
});
view code
<div ui-view="header"></div>
<div ui-view="content" class ='view ng-scope openm'></div>
please give me any idea. thanks
Think of it as a tree. The root of your tree is whatever module you set in the ng-app directive. When Angular it's bootstraping, it starts from there, and loads all of the module components and its dependencies' components. Once the bootstrap its finished, the lifecycle continues (creating constants, then providers, then running config blocks, etc).
Since somewhere in your app you have a dependency to ui-router, all of its services become available after bootstrap.
Regardless of that, you shouldn't rely on that for your modules to work. If you were to use your app.pages.core module in another application which doesn't have a dependency to ui-router, then it wouldn't work.
When you create an angular module, always specify the dependencies used in this module even if you already specified the same dependency on another module.
Remember that each module should be self sufficient and should work without relating on others module dependencies to be fully reusable.
So to answer correctly to your question .. yes you can use $stateProvider in any module, but add ui-router dependency in each of them.
Related
Here's the scenario:
I have a main AngularJS app that uses ui-router for routing, like any other app. I also have a smaller AngularJS module that functions as its own app, not requiring that it be a submodule of my larger app.
I would like for the smaller app to handle its own routing and templating, too. Goal here being, the mini app can be loaded by another AngularJS app, or loaded on its own, and have all its routing and templates set up already.
Main App:
angular
.module('mainApp')
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('mainAppParent', {
url: '/',
templateUrl: 'main.html',
controller: 'mainCtrl',
controllerAs: 'vm'
})
.state('mainAppParent.miniJournalApp', {
url: '/journal'
});
}]);
Mini App:
angular
.module('miniJournalApp')
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('mainJournalState', {
url: '/',
templateUrl: 'mainJournalView.html',
controller: 'JournalCtrl',
controllerAs: 'vm'
})
.state('newEntry', {
url: '/new'
});
}]);
I'm planning on having all the components of the mini app being an AngularJS .component(), so I'm wondering if the correct way to do this is to just let the parent app handle routing, and let the mini app handle the templates when I define each component/directive. Then, when I want to load the mini app on its own, I would just write a wrapper AngularJS module with new routing.
As we can see that in angular ui router we are using $stateProvider so basically we are using a provider.
In an angular app the provider is loaded once and then angular puts the instance in cache so next time when the provider is injected somewhere then same instance is used.
As same instance of $stateProvider is used across angular app so it is perfectly fine to define separate states for submodules.
And this is a good practice for code maintenance.
I have used it in many projects
I started my first angular application, and am running into an issue where my "home" module isn't working because of a dependency issue. I don't see any dependency missing that I would need. I am using $stateProvider, and $urlProvider, but I am injecting that into the configuration for the home module, so I'm not sure where the problem would lie ?
Config.$inject = ["$stateProvider", "$urlRouterProvider"];
angular.module('home', []).config(Config)
function Config($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/login',
templateUrl: './views/login.html'
})
}
angular.module('home').controller('loginCtrl', function($scope){
$scope.helloWorld = function(){
console.log("This works!")
}
})
The consoled error:
[$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?p0=home&p1=Error%3A%20…
Since "$stateProvider" and "$urlRouterProvider" providers are not part of the core AngularJS module, you need to inject modules, that have this provides into your home module definition. As far as I know, $stateProvider is from ui router module, so
angular.module('home', ['ui.router']).
...
Keep in mind that you also need to include this Javascript in your HTML file. It is in the angular-ui-router file
<script src="js/angular-ui-router.min.js"></script>
In case anyone else runs into the same issue. While trivial to some, it was not trivial for me.
The problem:
When the page loads ngRoute fails to load the shell (landing page) template when using ng-view inside ng-include.
However if we start navigating around the pages including navigating back to landing page then the template loads just fine. It is as if the first time the page loads it completely skips loading the landing template.
The setup:
Somewhere at the bottom of index.html's body:
<script src="angular-route.js"></script>
Somewhere in index.html template
<div ng-include="'root-shell.html'"></div>
Somewhere in root-shell.html template
<div ng-view></div>
The JS:
(function (/* IIFE enclosed code*/) {
angular('myApp', [$routeProvider]);
angular('myApp').config(function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'root-shell.html',
controller: 'RootShellController', // optional
controllerAs: 'rootShellCtrl' // optional
}).
.when('/about', {
templateUrl: 'about.html',
controller: 'AboutController', // optional
controllerAs: 'aboutCtrl' // optional
}).
otherwise({
redirectTo: '/'
});
});
})(/* IIFE invoke*/);
Why the problem?
Because if ng-view instantiation is delayed (through ng-include) then the $route instantiation is delayed as well. This means that $route will miss the location change event.
The fix:
Force a reload of the $route service.
angular('myApp').run(['$route', function($route) {
$route.reload();
}]);
Why does it work?
Because of angular's order of execution:
app config
app run
directive setup
directive compile
app controller
directive link
** Data resolve called **
new route's controller
and the run block:
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the services have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.
Disclaimer:
I am fairly new to Angular, therefore if I've misunderstood and/or misrepresented a concept then by all means chime in.
I have a main app on my application, called "bionico", this app utilizes UI-router
It has it's own $stateProvider configurations!
And that's fine, it works
BUT I needed to create a new module (cuz I'm trying to insert a step-by-step form on my application, you can find the code here: https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router)
To make this step-by-step form I need to create a new module with it's own $stateProvider configurations, the thing is that it doesn't work, I have included this module called "bionico.formApp" in my main app like this:
angular.module('bionico', ['ui.router', other modules, 'bionico.formApp'])
And that works fine, if I try to use a controller inside "bionico.formApp" it will also work fine. This is the code for this module:
// create our angular app and inject ngAnimate and ui-router
// =============================================================================
angular.module('bionico.formApp', ['ngAnimate', 'ui.router', ])
// configuring our routes
// =============================================================================
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('form', {
url: '/form',
templateUrl: 'templates/campaignForm/form.html',
controller: 'formCtrl'
})
// nested states
// each of these sections will have their own view
// url will be nested (/form/profile)
.state('form.profile', {
url: '/profile',
templateUrl: 'templates/campaignForm/form-profile.html'
})
// url will be /form/interests
.state('form.interests', {
url: '/interests',
templateUrl: 'templates/campaignForm/form-interests.html'
})
// url will be /form/payment
.state('form.payment', {
url: '/payment',
templateUrl: 'templates/campaignForm/form-payment.html'
})
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/profile');
})
// our controller for the form
// =============================================================================
.controller('formCtrl', function($scope) {
// we will store all of our form data in this object
$scope.formData = {};
$scope.test = "FUNCIONA";
// function to process the form
$scope.processForm = function() {
alert('awesome!');
};
});
But to make this form work I need to use <div ui-view></div> and when I put this on my file nothing happens, and I think it's because the app is using my main module "bionico" $stateProvider configurations and not "bionico.formApp"
Here is my html code:
<div ng-controller="formCtrl">
<!-- views will be injected here -->
<div ui-view></div>
{{test}}
the {{test}} from formCtrl works fine, it is printing the variable on my screen, but like I said ui-view is not. Is there a way to tell angular that I want to use "bionico.formApp" configurations and not the configs from my main module?
How would you solve this?
Like I think I have to tell angular that from that moment on I want to use "bionico.formApp" but I don't know how to do it.
I've tried
<div ng-app="bionico.formApp"> but nothing happened, not even errors
I saw then on stack over flow that I have to bootstrap it manually, but then I got an error saying that "bionico.formApp" had already been bootstraped
Update
People suggested that I only needed to rout stuff in only one module, but I needed two different default routs one for my main application and one or my form, but that wouldn't work.
and the reason why I was trying to make this work was because I needed a step by step form (wizard) on my application like this one: https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router)
But I've decided it is not worth the hassle, I'll try to find another wizard for angular js
I'm using now this walktrough wizard:
https://github.com/mgonto/angular-wizard
It was easy to install and it is very easy to customize the template, I recommend!
<html ng-app="app">
<head>
...
</head>
<body>
<div id="header"></div>
<div id="notification"></div>
<div id="container"></div>
<div id="footer"></div>
</body>
</html>
With the given structure of the app (derived from angular-app):
header: Here the site navigation, login/out toolbar etc comes in. This is dynamic and has it's own Controller
notification: Global notification container.
container: This used to be my <ng-view>. So this is where all other modules loads in.
footer: Global footer.
How do the state hierarchy looks like? I've gone through the example which shows a single module (contacts) but typically an app would have a global (root) state and inside the root state other module states are rendered.
What I'm thinking is my app module probably have the root state and then each module should have it's own state and I have to inherit from root state. Am I right?
Also from ui-state example, they have used both $routeProvider and $urlRouterProvider as well as $stateProvider has url defined. My understand was $stateProvide also handles routing. If I'm wrong, which provider should I use for routing?
EDIT: http://plnkr.co/edit/wqKsKwFq1nxRQ3H667LU?p=preview
Thanks!
The approach you took in your plunker is close. #ben-schwartz's solution demonstrates how you'd set defaults in your root state for the three essentially-static views. The thing missing in your plunker is that your child states still need to reference the top container view.
.state('root',{
url: '',
views: {
'header': {
templateUrl: 'header.html',
controller: 'HeaderCtrl'
},
'footer':{
templateUrl: 'footer.html'
}
}
})
.state('root.about', {
url: '/about',
views: {
'container#': {
templateUrl: 'about.html'
}
}
});
Note views: { 'container#': ...} instead of just templateUrl: ... in 'root.about'
What you may also be asking about is whether you can have modules define their own state-sets, which you then attach to your app's state hierarchy. A sort of plug-and-play for the routes/states each module provides.
To achieve this you'll have tightly couple your modules to your main app.
In the module:
angular.module('contact', ['ui.router'])
.constant('statesContact', [
{ name: 'root.contact',
options: {
url: 'contact',
views: {
'container#': {
templateUrl: 'contacts.html'
}
},
controller:'ContactController'
}}
])
.config(['$stateProvider', function($stateProvider){
}])
Then, in the app:
var app = angular.module('plunker', ['ui.router', 'contact']);
app.config( ['$stateProvider', 'statesContact',
function($stateProvider, statesContact){
$stateProvider
.state('root',{ ... })
.state('root.home', { ... })
.state('root.about', { ... })
angular.forEach(statesContact, function(state) {
$stateProvider.state(state.name, state.options);
})
}]);
This means all your modules will need to be compatible with this pattern set out in your app. But if you accept this constraint you can then choose include any combination of your modules, and their states magically get added to your app. If you want to get even fancier, you can modify state.options.url in your statesModuleName loop to, for example, prefix your module's url structure.
Also note that the module ui.compat is only necessary when you are transitioning from $routeProvider to $stateProvider. You should normally use ui.state instead.
Also don't forget to adjust in header.html $state.includes('root.contact'))
updated plunker
Although confusing, the FAQ in the ui-router wiki seems to say that this isn't possible: ui-router/wiki/faq
One approach is to allow each feature to define it's own root state (as in this example: AngularJS State Management with ui-router)
Another would be to define the entire state hierarchy in your "myApp" module and just leverage controllers etc. from the dependent modules. This works especially well when you are maintaining a mobile and desktop site; define each site's state hierarchy in a mobileApp and desktopApp module, and have the functionality (e.g. controllers, services, etc.) shared in separate modules.
It depends how you prefer to approach it.
All scopes inherit from rootScope so you may place global state there. The NG literature specifically mentions that you should only put global state there, and not functionality. Functionality required across the system belongs in services, and specifically you should not implement services whose sole purpose is to retain state. All the advice seems to be implicitly shared in the context of how it either facilitates or hampers your ability to easily do end to end testing.