Properly loading controller path in ui-router - javascript

I'm working on a complex Angular app and I'd like to know if there is a proper way to load a controller file path in an ui-router.
Currently, I load every controllers file in the main view like this : <script src="myCtrl.js"></script>.
I know that I can use ui-router like this :
$state provider
.state('state1'), {
url: '/state1',
views : {
'area1' : {
templateUrl: 'myView.html',
controller: 'myCtrlName' // change this to load the file ?
},
... // some other area + templateUrl here
}
}
But it's used to load the controller name, not its path. I tried to find a way by using load or file functions instead, but it didn't make it.
Do you have any idea ?
Edit : Maybe it can help you to know thatmy goal is to load my controller file out of the html view in order to simplify the html file and simply add/delete controllers in the app.

According to Radim Köhler and these questions angular-ui-router with requirejs, lazy loading of controller + AngularAMD + ui-router + dynamic controller name?, I'll try to use RequireJS.
Thanks a lot guys :)

Related

Lazy loading controller from separate file using state provider

So, I'm trying to dynamically load a file that adds a controller to my main module. The thing is that I don't want to reference the file itself in a script tag.
To be more specific, I have 2 controller files. The main controller file mainController.js referenced in my index.html file. Any controller that I add to that file loads without issues.
The controller file I want to use for my login page login.js, contains the following information:
function LoginCtrl($http){
console.log("Controller loaded");
};
angular
.module('inspinia')
.controller('LoginCtrl', LoginCtrl);
All my controller files are in the same folder, however, contrary to the mainController.js, the login.js file does not appear inside any .html file.
My intention was to load the login.js file dynamically inside my stateProvider like this:
$stateProvider
...
.state('logins', {
url: "/logins",
templateUrl: "views/login.html",
controller: LoginCtrl,
data: { pageTitle: 'Login', specialClass: 'gray-bg' },
resolve: {
loadPlugin: function($ocLazyLoad) {
return $ocLazyLoad.load ({
name: 'inspinia.LoginCtrl',
files: ['js/controllers/login.js']
});
}
}
})
...
So long as I dont try to dynamically load the login.js file (so adding a reference to the file in a .html file or adding the login controller code inside the mainController.js file) everything works. But as soon as I try to remove the references to force the stateProvider to take care of the loading I get an Error: $injector:modulerr
Anyone knows what's the proper way to call the lazyLoader so I can only load my .js files when I need them?
EDIT info:
Something that I forgot to mention: The file-loading part itself seems to be working. If I do not call the controller anywhere and only load it. I can see the controller file being loaded by the browser. But the problem seems to be a timing issue. If I mention the controller name inside the .state() angular tries to access it before it gets loaded and the whole thing crashes before even loading the file
I recomend you to look over the ocLazyLoad to see how a controller is declared and loaded with ui-router resolve state property:
https://oclazyload.readme.io/docs/with-your-router
Basically, I think that what is missing in your approach is to use the string controller declaration, not the function one:
$stateProvider
...
.state('logins', {
url: '/logins',
templateUrl: 'views/login.html',
controller: 'LoginCtrl as login',
data: {
pageTitle: 'Login',
specialClass: 'gray-bg'
},
resolve: {
loadPlugin: function($ocLazyLoad) {
return $ocLazyLoad.load('js/controllers/login.js');
}
}
})
...
A tip that is important to use is: simplify the first implementation of something that you didn't used before. Your example shows a lot of parameters on the ocLazyLoad service. Try to load first the main element that you need, progressively adding one after the other after it succeeds because, sometimes, you may be on the right track and something like this, which you're unaware of, can lead you to a loooooong debug routine.
Also, take a look at the example below:
https://github.com/ocombe/ocLazyLoad/tree/master/examples/complexExample
It has a state declaration very similar to yours. Compare each other and modify to suit your needs.

Angular JS how to get templateUrl in Directive

I am a starter in AngularJS. Now I am facing a problem. I build a web in spingMVC and AngularJS. I want to use directive to display my header part. When I write the directive templateUrl, I always get Not Found(404). Is there anybody can tell me how to get the correct path here. There is my web application structure.
My dispathcer-servlet file is:
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"/>
<mvc:resources mapping="/view/**" location="/WEB-INF/view/"/>
<mvc:resources mapping="/customJS/**" location="/WEB-INF/customJS/"/>
In main.js I write:
app.directive('naviDirective', function(){
return {
retstrict: 'EA',
templateUrl: '/view/header.jsp',
controller: function(){
console.log("hea[enter image description here][1]der");
}
};
});
In home.jsp I write:
<navi-directive></navi-directive>
When I load home.js page, the console can print "header", but I get
"angular.min.js:103 GET http://localhost:8080/view/header.jsp 404 (Not Found)".
Who can tell me how to fix this path. Thank you very much

Angular + RequireJs: Resolve templateUrls of directives

I use angularjs and requirejs in my spa. For the organization of imports and so on I use require. In requirejs I can use e.g. baseUrl: Every import path is resolved with the baseUrl. Now I would like to resolve the templateUrls the same way. Therefore I can use e.g.:
templateUrl = requirejs.toUrl("modules/test/chuck.directive.html")
The problem that I would like to resolve every templateUrl of every directive this way.
So: Is there a possibility to jump into the template loading process of directives in angular and run the above code?
Thanks for any hint.
I would decorate $templateRequest service if you know for sure that you want to intercept all template loading requests and modify template URL. Something like this:
.config(function($provide) {
$provide.decorator('$templateRequest', function($delegate) {
return function(tpl, ignoreRequestError) {
tpl = requirejs.toUrl(tpl); // modify original tpl
return $delegate.call(this, tpl, ignoreRequestError);
};
});
})

Best practice for including scripts in Angular templates?

I have an Angular SPA with several tabs, which are served by templates. Each of these tabs requires different scripts (both local and CDN), so I'd only like to load scripts when needed, ie I will need to include them inside the templates (or associated controllers) themselves.
So far, I've tried this approach:
// Start template
<data-ng-include src="http://maps.googleapis.com/maps/api/js"></data-ng-include>
// Rest of stuff
// End template
This doesn't seem to be working. Yes, I have jQuery included in the header. What is the best way to go about this? Seems like it should be much simpler to include scripts inside templates.
You can use any of the lazy loader (on demand loader) library like requireJS, ocLazyLoad.
I have successfully implemented ocLazyLoad in one of our enterprise application, because it provide lots of usefull features if you are developing modular Single Page Application:
Easy to implement. You can lazy load any resource anywhere inside your application
Dependencies are automatically loaded
Debugger friendly (no eval code)
Can load any client side resouce like js/css/json/html
Compatible with AngularJS 1.2.x/1.3.x/1.4.x
You can also load any resource inside service, factory, directive also.See example
Resolve any resource before executing any state if you are using ui.router library for routing.See example
You can also lazy load resource in .config() of any module.See example
It also gives you the functionalty of logging module loading events.
All references are taken from official website of ocLazyLoad
If you want to include JS. I would tell you , please use :
cLazyLoad JS
Here is example:
var myapp = angular.module('myApp', ['oc.lazyLoad']);
myApp.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
template: '<div ui-view class="ngslide"></div>',
resolve: {
deps: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load([js/jquery.main.min.js','js/ie10-viewport-bug-workaround.js']);
}]
}
})
});
I use a simple directive for this (here :
interface AddScriptDirectiveAttributes extends IAttributes {
type: string;
src: string;
}
export function addScript(): IDirective {
return {
restrict: 'E',
link: function (scope: IScope, element: JQuery, attrs: AddScriptDirectiveAttributes) {
let script = document.createElement('script');
if (attrs.type) {
script.type = attrs.type;
}
script.src = attrs.src;
document.body.appendChild(script);
}
};
}
Usage would be something like this:
<add-script src="script" type="application/javascript"></add-script>

Tried to Load Angular More Than Once

I have a yeoman scaffolded app (the angular fullstack generator).
grunt serve works fine, but grunt build produces a distribution that locks up memory, most probably because of circular references in angular.
I upgraded angular to 1.2.15. The error I get is:
WARNING: Tried to Load Angular More Than Once
Prior to upgrading, the error was:
Error: 10 $digest() iterations reached. Aborting!
It's pretty difficult to debug as it only happens after build / minification. All my modules are in angular's array format, so the minification DI shouldn't be a problem but it is.
There's no single script that causes this. The only way it goes away is if I don't initialize with my app.js file. My app.js file is below.
Any thing come to mind?
'use strict';
angular.module('myApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'ngTagsInput',
'ui.bootstrap',
'google-maps',
'firebase'
]);
angular.module('myApp').config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/listing.html',
controller: 'ListingCtrl'
})
.otherwise({
redirectTo: '/'
});
}]).constant('FIREBASE_URL', 'something');
This could be a number of issues: essentially it's a problem of routeProvider not finding a file and recursively loading the default.
For me, it turned out that it wasn't minification but concatenation of the js that caused the problems.
angular.module('myApp').config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/listing.html',
controller: 'ListingCtrl'
})
.otherwise({
redirectTo: '/'
});
}]).constant('FIREBASE_URL', 'something');
You'll notice that if the app can't find a file (i.e., otherwise), then it will redirect to the root, which in this case loads the templateUrl. But if your templateUrl is wrong, then it will cause a recursion that reloads index.html loading angular (and everything else) over and over.
In my case, grunt-concat caused the templateUrl to be wrong after build, but not before.
The problem could occur when $templateCacheProvider is trying to resolve a template in the templateCache or through your project directory that does not exist
Example:
templateUrl: 'views/wrongPathToTemplate'
Should be:
templateUrl: 'views/home.html'
This doesn't have anything to do with app.js at all. Instead, this warning is logged when you include the Angular JS library more than once.
I've managed to reproduce the error in this JSBin. Note the two script tags (two different versions):
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
Relevant Angular code at GitHub.
Seems like nobody has mentioned this anywhere so here is what triggered it for me:
I had the ng-view directive on my body. Changing it like so
<body layout="column">
<div ng-view></div>
...
</body>
stopped the error.
I was also facing such an issue where I was continously getting an infinite loop and the page was reloading itself infinitely. After a bit of debugging I found out that the error was being caused because, angular was not able to load template given with a particular id because the template was not present in that file.
Be careful with the url's which you give in angular apps. If its not correct, angular can just keep on looking for it eventually, leading to infinite loop!
Hope this helps!
I had the same issue, The problem was the conflict between JQuery and Angular. Angular couldn't set the full JQuery library for itself. As JQLite is enough in most cases, I included Angular first in my web page and then I loaded Jquery. The error was gone then.
In my case I was getting this error while using jquery as well as angular js on the page.
<script src="http://code.jquery.com/jquery-2.1.3.min.js" type="text/javascript"></script>
<script src="js/angular.js" type="text/javascript"></script>
<script src="js/angular-route.js" type="text/javascript"></script>
I removed :
<script src="http://code.jquery.com/jquery-2.1.3.min.js" type="text/javascript"></script>
And the warning disappeared.
Had this problem today and figured I would post how I fixed it. In my case I had an index.html page with:
<body ng-app="myApp" ng-controller="mainController"
<div ng-view></div>
</body>
and in my app.js file I had the following code:
$routeProvider.when('/', {
controller : 'mainController',
templateUrl : 'index.html',
title : 'Home'
}).when('/other', {
controller : 'otherController',
templateUrl : 'views/other.html',
title : 'other'
}).otherwise({
redirectTo : '/'
});
As a result, when I went to the page (base_url/) it loaded index.html, and inside the ng-view it loaded index.html again, and inside that view it loaded index.html again.. and so on - creating an infinite recursive load of index.html (each time loading angular libraries).
To resolve all I had to do was remove index.html from the routProvider - as follows:
$routeProvider.when('/other', {
controller : 'otherController',
templateUrl : 'views/other.html',
title : 'other'
}).otherwise({
redirectTo : '/'
});
I had a similar issue, and for me the issue was due to some missing semicolons in the controller. The minification of the app was probably causing the code to execute incorrectly (most likely the resulting code was causing state mutations, which causes the view to render, and then the controller executes the code again, and so on recursively).
I had that problem on code pen, and it turn out it's just because I was loading JQuery before Angular. Don't know if that can apply for other cases.
Capitalization matters as well! Inside my directive, I tried specifying:
templateUrl: 'Views/mytemplate'
and got the "more than once" warning. The warning disappeared when I changed it to:
templateUrl: 'views/mytemplate'
Correct me, but I think this happened because page that I placed the directive on was under "views" and not "Views" in the route config function.
This happened to me too with .NET and MVC 5 and after a while I realized that within the label on Index.cshtml file:
<div data-ng-view=""></div>
again included as section scripts happens to you. To solve the problem on the server side what I do is return the partial view. Something like:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
return PartialView();
}
public ActionResult About()
{
return PartialView();
}
}
I had this same problem ("Tried to Load Angular More Than Once") because I had included twice angularJs file (without perceive) in my index.html.
<script src="angular.js">
<script src="angular.min.js">
I have the same problem, because I have angular two times in index.html:
<script src="https://handsontable.github.io/ngHandsontable/node_modules/angular/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
Note that the warning arises only when html5 mode is true, when my html5 mode was false, I did not see this warning.
So removing the first angular.js solves the problem.
You must change angular route '/'! It is a problem because '/' base url request. If you change '/' => '/home' or '/hede' angular will good work.
For anyone that has this issue in the future, for me it was caused by an arrow function instead of a function literal in a run block:
// bad
module('a').run(() => ...)
// good
module('a').run(function() {...})
In my case I have index.html which embeds 2 views i.e view1.html and view2.html. I developed these 2 views independent of index.html and then tried to embed using route.
So I had all the script files defined in the 2 view html files which was causing this warning. The warning disappeared after removing the inclusion of angularJS script files from views.
In short, the script files angularJS, jQuery and angular-route.js
should be included only in index.html and not in view html files.
Another case is with Webpack which concating angular into the bundle.js, beside the angular that is loaded from index.html <script> tag.
this was because we used explicit importing of angular in many files:
define(['angular', ...], function(angular, ...){
so, webpack decided to bundle it too. cleaning all of those into:
define([...], function(...){
was fixing Tried to Load Angular More Than Once for once and all.
My problem was the following line (HAML):
%a{"href"=>"#", "ng-click" => "showConfirmDeleteModal()"} Delete
Notice that I have a angular ng-click and I have an href tag which will jump to # which is the same page. I just had to remove the href tag and I was good to go.
The problem for me was, I had taken backup of controller (js) file with some other changes in the same folder and bundling loaded both the controller files (original and backup js). Removing backup from the scripts folder, that was bundled solved the issue.
I had this problem when missing a closing tag in the html.
So instead of:
<table></table>
..my HTML was
<table>...<table>
Tried to load jQuery after angular as mentioned above. This prevented the error message, but didn't really fix the problem. And jQuery '.find' didn't really work afterwards..
Solution was to fix the missing closing tag.
I was having the exact same error. After some hours, I noticed that there was an extra comma in my .JSON file, on the very last key-value pair.
//doesn't work
{
"key":"value",
"key":"value",
"key":"value",
}
Then I just took it off (the last ',') and that solved the problem.
//works
{
"key":"value",
"key":"value",
"key":"value"
}

Categories