I have two different login views login.html and adminLogin.html so in Angular app.js I added first one as:
$stateProvider
.state('login', {
url: "/login.html",
templateUrl: "../login.html",
controller: "login",
authenticate: false,
resolve: {
deps: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'myapp',
insertBefore: '#ng_load_plugins_before', // load the above css files before a LINK element with this ID. Dynamic CSS files must be loaded between core and theme css files
});
}]
}
})
and it runs correctly, problem is when I try to add second one below this one as:
.state('login', {
url: "/adminLogin.html",
templateUrl: "../adminLogin.html",
controller: "adminLogin",
authenticate: false,
data: { pageTitle: 'INICIAR SESIÓN' },
resolve: {
deps: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'myapp',
insertBefore: '#ng_load_plugins_before', // load the above css files before a LINK element with this ID. Dynamic CSS files must be loaded between core and theme css files
}]
}
})
When I try to acces first one or new one it only show blank page
Note**: if I comment last code, first one runs correctly again.
Some one know what is wrong there? Help is very appreciated. Regards
You have two states with the same name: login. The UI-Router has no idea what to do in that situation. Try re-naming your second state to something like adminLogin.
Related
I'm new to Angular 1 and have to implement a new feature on an existing webapp. The app uses jhipster to generate some parts of the backend and frontend (Angular 1 and uirouter).
So I tried to use my own route and state like this which is mostly copy and pasted from existing components of the webapp:
(function() {
'use strict';
angular
.module('artemisApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider
.state('model-comparison-exercise-for-course', {
parent: 'entity',
url: '/course/{courseid}/model-comparison-exercise',
data: {
authorities: ['ROLE_ADMIN', 'ROLE_TA'],
pageTitle: 'artemisApp.modelComparisonExercise.home.title'
},
views: {
'content#': {
templateUrl: 'app/entities/model-comparison-exercise/model-comparison-exercise.html',
controller: 'ModelComparisonExerciseController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('modelComparisonExercise');
$translatePartialLoader.addPart('exercise');
$translatePartialLoader.addPart('global');
return $translate.refresh();
}],
courseEntity: ['$stateParams', 'Course', function ($stateParams, Course) {
return Course.get({id: $stateParams.courseid}).$promise;
}]
}
});
}
})();
Then I try to open this route with the following code:
<a ui-sref="model-comparison-exercise-for-course({courseid:course.id})"
data-translate="artemisApp.course.modelComparisonExercises"></a>
By clicking on that link a http get request is fired which returns a http status code 404: http://localhost:8080/app/entities/model-comparison-exercise/model-comparison-exercise.html
Actually, the url that should be opened is http://localhost:8080/#/course/1/model-comparison-exercise
Any idea what I could have configured wrong?
Please try changing 'content#' to 'content#artemisApp'.
As explained here:
The symbol before the # is the name of the view you want to match, and the symbol after the # is a reference to the state in which the template the ui-view directive should exist in.
And the <a> tag is not being closed:
<a ui-sref="model-comparison-exercise-for-course({courseid:course.id})"
data-translate="artemisApp.course.modelComparisonExercises"></a>
Searching through the code, I found that model-comparison-exercise.html does not exist in folder model-comparison-exercise. Besides model-comparison-exercises.html exist.
I would like to load a file when a specific URL is hit, but I didn't have any success with it yet:
This is how my code looks like:
.state('admin.resources', {
url: '/resources',
templateUrl: 'views/admin/resources.html',
controller: 'ResourceController as res',
resolve: {
loadMyFiles: function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'venture',
files: [
'bower_components/angular-ui-tinymce/src/tinymce.js'
]
})
}
}
})
It works all fine if I include it on the index.html like this:
<script type="text/javascript" src="bower_components/angular-ui-tinymce/src/tinymce.js"></script>
I don't want to load id from index.html as I don't want that page to be loaded once the site is loaded. I need that script on a separate page.
Any suggestion?
In order to use the oclazyload, you need to put it in the resolve property:
$stateProvider.state('index', {
url: "/", // root route
views: {
"lazyLoadView": {
controller: 'AppCtrl', // This view will use AppCtrl loaded below in the resolve
templateUrl: 'partials/main.html'
}
},
resolve: { // Any property in resolve should return a promise and is executed before the view is loaded
loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
// you can lazy load files for an existing module
return $ocLazyLoad.load('js/AppCtrl.js');
}]
}
});
All is explained and well-documented in With your router.
I'm currently working on a project using UI Router. My code currently defines states as part of the app config ( example below ) but the code is growing. Is there a good way to modularize this code both for organization and unit testing? For me the best solution would be to define states as an external service.
.state('page', {
url: '/page/{id}',
params: {
id: ['$q', function ($q) {
// Code
return defaultValue;
}],
},
templateUrl: 'page.html',
'controller': 'CatalogDetailsController',
'controllerAs': 'details',
resolve: {
categories: ['$q', function ($q) {
// Code
return promise;
}],
},
I would start by defining the objects separately instead of in-line
( do note that this does make the code less readable)
.state('page', {
url: '/page/{id}',
params: myparamsObj // defined somewhere else.
templateUrl: 'page.html',
'controller': 'CatalogDetailsController',
'controllerAs': 'details',
resolve: myResolveObj, // defined somewhere else.
If your app.config is becoming too big , you could use the approach mentioned in [refactor large AngularJS module config into separate files ] question to split your config part.
My proposal is based on ES6 modules.
Long story short. Each state has a separate folder, for instance contacts/one/edit. In this folder I have the following files:
controller.js, controller.spec.js
state.js, state.spec.js
state.html
state.js holds the state definition object:
import controller from './edit.controller';
import template from './edit.state.html';
// State name is exported so we can use it in the corresponding tests
export const name = 'contacts.one.edit';
export default {
name,
url: '/edit',
template,
controller,
controllerAs: 'ctrl'
};
This configuration could be activated in the module's configuration block:
import oneState from './one/one.state';
export function states($stateProvider) {
'ngInject';
$stateProvider
.state({
parent: 'app',
name: 'contacts',
abstract: true,
url: '/contacts',
template: '<div ui-view autoscroll="true"></div>'
})
.state(oneState)
.state(oneEditState)
// etc...
}
Here you will find the complete example https://github.com/lucassus/angular-webpack-seed/tree/ce4e9b91ce9ed47ca74073d754b0cbacff8cb65f/src/app/contacts/one/edit
WikiApp.config(function config($stateProvider, $urlRouterProvider) {
$stateProvider
.state('revision', {
url: '/wiki',
views: {
"main": {
controller: 'ListCtrl',
templateUrl: 'wiki/wiki.tpl.html'
},
"sidebar-left": {
templateUrl: 'wiki/wiki.sidebar-left.tpl.html'
}
},
data:{ pageTitle: 'List articles' }
})
This is what my Angular bit looks like and this is how I execute it inside of a template (wiki.tpl.html):
<div ui-view="sidebar-left"></div>
Now the main view works fine, but as I try to integrate the sidebar, it doesn't load, what am I doing wrong and how can I use more than one template in a single page like this?
Thank you!
WikiApp.config(function config($stateProvider, $urlRouterProvider) {
$stateProvider
.state('revision', {
url: '/wiki',
views: {
main: {
controller: 'ListCtrl',
templateUrl: 'wiki/wiki.tpl.html'
},
sidebarLeft: {
templateUrl: 'wiki/wiki.sidebar-left.tpl.html'
}
},
data:{ pageTitle: 'List articles' }
})
If you want to use nested templates you should implement that using sub-views. In your current example you are setting both templates as sibling templates.
I suggest you to create 2 states. Abstract view for the main template 'main' and another view 'main.wiki'. Route should be assigned to 'main.wiki' state ant it will inherit parameters from the main view (including template settings).
Hope that's clear.
Does angularJS have anything in place to lazyload external JS/CSS files based on ng-controller namespaces? So that the following would append com.myApp.SomeClass.js and com.myApp.SomeClass.css to the document head?
<div ng-controller="com.myApp.SomeClass"></div>
Not yet, but it is in the works post v1.0.
Is your app so big that you need it? We have some impressively big apps, and have not run into this need yet, since the controllers are much more dense then when writing the same behavior without angular.
how about using slowscript? It's really easy to lazyload on angularjs
Example:
https://github.com/flrngel/slowscript-angular-require-lazyload
Slowscript:
https://github.com/flrngel/slowscript
core code from website
app.js
app = angular.module("mainApp", ["ui.router"]).run(function($rootScope){
$rootScope.$on('$viewContentLoaded',function(){
slowscript.execute();
});
});
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider.state("sexy", {
url: "/sexy",
views: {
"contents": {
templateUrl: "/views/test.html",
controller: "Sexy",
reloadOnSearch: true
}
}
});
});
app.controller("Sexy", function($scope) {
slowscript.queue(function(){
slowscript.$global.sexy($scope);
});
});
test.html (angular view template)
<div ng-controller="Sexy">
<input list="univ">
<datalist id="univ">
<option ng-repeat="univ in univs" value="{{univ.name}}"></option>
</datalist>
</input>
<input type="submit"></input>
<noscript src="test.js" type="text/slowscript"></noscript>
</div>
test.js
require(['angular','slowscript'],function(angular,slowscript){
slowscript.$global.sexy=(function($scope){
console.log("tada~");
$scope.univs = [{"idx": 1,"name": "asdf"}, {"idx": 2,"name": "bsdf"}];
$scope.$apply();
});
slowscript.queue_execute();
});
It not possible with stock AngularJS 1.x
However, you can use $oclazyload to defer the loading of code files (js, HTML, CSS)
$ocLazyLoad works perfectly with routers like UI-Router. It returns a promise and uses resolve property to make sure that files are loaded before the view is resolved.
angular.module('myApp', ['ui.router', 'oc.lazyLoad'])
.config(function($stateProvider, $urlRouterProvider, $ocLazyLoadProvider) {
$stateProvider
.state('stateA', {
url: '/stateA',
controller: 'StateACtrl',
templateUrl: 'stateA.html',
resolve: {
load: [ '$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([
{
name: 'stateA',
files: [
'stateA.css',
'stateA.js',
],
},
]);
},
],
},
})
.state('stateB', {
url: '/stateB',
controller: 'StateBCtrl',
templateUrl: 'stateB.html',
resolve: {
load: [ '$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([
{
name: 'stateB',
files: [
'stateB.css',
'stateB.js',
],
},
]);
},
],
},
});
});
I have also created a plunkr to demo a working model.
Documentation to know more about possible config options.
Hope it helps!