How to use Lazylaoding in angularjs - angularjs

How I can use On demand loading of controllers /services in angularJs. now I have a mainApp which referenced by Index.html. I am using $routeprovider to routing but all the required controller/services are referenced in the corresponding views eg:
<section id="pageLevelScripts">
<!--location of page level scripts-->
<script src="/Area/Sotaria/Controllers/UserController.js"></script>
</section>
<div class="row" >
<div class="col-lg-12">
<div class="row">
<div class="col-lg-12">
<ol class="breadcrumb">
<li>Home</li>
<li>Usermanagement</li>
<li class="active"><span>Users</span></li>
</ol>
<h6>{{name}}</h6>
</div>
</div>
This is my requirement I don't want to load all the controllers in my Index.html. but when I navigate to the childView, I am getting an error "Usercontroller is undefined", So how I can enable ondemanidng / lazy loading of controllers in angualrjs, .Ie. when ever the child page loaded ,then only its related controllers should load.

The reason you get undefined is because just loading the JS file doesn't make Angular aware of it. You must attach it to Angular's $controllerProvider.
You can create a custom service that does the lazy loading for you or use one of the many already built by other people. Here is an example of a custom one that you can use with requirejs.
If the below example is not explanatory enough you can read this blog post here: Strange Milk - Front End Development
Custom Provider
function LoaderProvider(){
this.controller = null;
this.directive = null;
this.filter = null;
this.factory = null;
this.service = null;
//service factory definition
this.$get = ['$q', '$rootScope', function($q, $rootScope){
return {
load: function (path) {
var q = $q.defer();
require([path], function () {
$rootScope.$apply(function () {
q.resolve();
});
});
return q.promise;
}
};
}];
}
var container = new LoaderProvider();
module.provider('Loader', function(){
return container;
});
Your Apps config:
.config(function (LoaderProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
// save references to the providers
LoaderProvider.controller = $controllerProvider.register;
LoaderProvider.directive = $compileProvider.directive;
LoaderProvider.filter = $filterProvider.register;
LoaderProvider.factory = $provide.factory;
LoaderProvider.service = $provide.service;
})
An example of how you would use this in your App's routes. Learn more about Resolve
resolve : {
load : ['Loader', function(Loader){
return Loader.load('../app/global/controllers/headerCtrl');
}]
}
Some other resources/solutions:
ocLazyLoad
angularAMD

Related

Angular - UI.Router not loading component

I am unable to get ui.router to load a component on index.html, through a localhost or otherwise:
index.html
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="//unpkg.com/angular-ui-router#0.3.1/release/angular-ui-router.js"></script> <!-- used to route views -->
<script src="js/components.js"></script>
<body ng-app="myApp">
<div> <!-- main content -->
<ui-view></ui-view> <!-- this directs ui-router to put the home view here-->
</div>
components.js
var app = angular.module('myApp', ['ui.router']); //makes a call to the Angular API to create our app, configured with the ui.router plug-in
//configure our view router
app.config(function($stateProvider) {
//create our different views as objects
var mainState ={
name: 'main', //name of the object
url: '', //url to point to, or that causes this view to be triggered
component: 'home', //component that works with the data, loads the template
resolve: { //data to bind to our component
data: function(Resource) {
console.log(Resource.test()) //returns "Hello World"
return Resource.test()
}
}
}
//call the states
$stateProvider.state(mainState);
})
app.service('Resource', function ($http) {
var service = {
test: function() {
return "Hello world"
}
}
return service;
})
app.component('home', {
bindings: { data: '<'}, //make the data we loaded into the view from the factory available to this component
template: "<p>{{data}}</p>", //this is the html that we will plug our data into
controller: function () {
console.log(this) //does not call
}
})
"Hello world" loads, as does data when I make $http gets through the service. But it does not get passed to the component. Any thoughts?
component attribute is available from ui-router#1.0.0(see here and in CHANGELOG.MD - it was added in 1.0.0-aplpha) so it's not available 0.3.1.
Here is working jsFiddle (with 1.0.3-beta)
By default the data will be applicable on view with $ctrl controller alias
template: "<p>{{$ctrl.data}}</p>",
Demo Plunkr

Get Url parameter in angularjs

In my MVC 5 application I want to use some AngularJS. Now, I have a project card. You can call this card by following the url:
http://localhost/Project/Card/1
For this card, I have a list of projectTasks. Based on the id = 1 parameter I want AngularJS to show this list, but I don't know how to get the id parameter. I was looking at ngRoute, but I don't want to route I only want to construct a dynamic url in my ProjectTaskController.
What I have so far:
projectApp.js
(function () {
var app = angular.module("projectApp", ["ngRoute"]
.config(function ($routeProvider, $locationProvider) {
//configure the routing rules here
$routeProvider.when('/Card/:id', {
controller: 'ProjectTaskCtrl'
});
// enable HTML5mode to disable hashbang urls
$locationProvider.html5Mode(true);
})
}());
ProjectTask.js
(function (app) {
var projectTaskCtrl = function ($scope, $routeParams) {
alert($routeParams.id); // message: undefined
};
app.controller("ProjectTaskCtrl", projectTaskCtrl);
}(angular.module("projectApp")));
I've solved it by using ng-init:
in my MVC5 View (Show.cshtml) I added:
<div id="projectTasks" ng-app="ProjectApp">
<div ng-controller="ProjectTaskCtrl">
<input type="hidden" id="projectId" ng-model="projectId" ng-init="projectId=#ViewBag.projectId"/>
<!-- some more code -->
</div>
</div>
In my ProjectTaskCtrl.js:
(function (app) {
var projectTaskCtrl = function ($scope) {
$scope.$watch("projectId", function(){
// I can now use projectId in whatever I want:
alert($scope.projectId);
});
};
app.controller("ProjectTaskCtrl", projectTaskCtrl);
}(angular.module("projectApp")));

How to setup angularjs in a different file in DNN 8.0 SPA framework?

I'm developing in DNN 8.0 with its new SPA framework.
I'm using angularjs instead of the defaulted KnockOut.
When I put module setup code in view.html, it works fine. But it doesn't work if I try to put the code in a different file, say app.js. Anyone has seen this before?
A snap shot of the view.html that works looks like this:
[JavaScript:{ jsname: "JQuery" }]
[JavaScript:{ path: "~/Resources/Shared/scripts/dnn.jquery.js"}]
[JavaScript:{ path: "~/DesktopModules/DNNSPA/Scripts/angular.js"}]
[JavaScript:{ path: "~/DesktopModules/MyApp/Scripts/angular-route.js"}]
[JavaScript:{ path: "~/DesktopModules/MyApp/ngApp/app.js"}]
[JavaScript:{ path: "~/DesktopModules/MyApp/ngApp/ngControllers/FirstController.js"}]
[CSS:{ path: "~/DesktopModules/MyApp/CSS/bootstrap.min.css"}]
<script type="text/javascript">
var d = new Date();
var moduleId = parseInt("[ModuleContext:ModuleId]");
var portalId = parseInt("[ModuleContext:PortalId]");
var sf = $.ServicesFramework(moduleId);
var moduleName = "MyApp";
if ("[ModuleContext:EditMode]" === 'True') {
var editMode = true;
}
console.log(editMode);
var currentDate = d;
var app = angular
.module('dnnapp', [
'ngRoute'
])
.config(
function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: '/DesktopModules/MyApp/partials/firstView.html',
controller: 'firstController'
});
});
app.controller('firstController', function ($scope, $http) {
// controller code
});
</script>
<div class="module-wrap" ng-view>
</div>
All setup code is in view.html.
But if I move the module definition to app.js and controller definition to FirstController.js, it doesn't work.
I've seen others make it work in different files, what am I doing wrong here?
The SPA tokens, ie: [ModuleContext:ModuleId] will not get replaced in your .js file. Therefore you need to do some setup work for your angular module on the page and move all the rest to a js file. Here is one technique:
On your View.html, define an html element for your angular app with an ng-init() method:
<div id='dnnuclear-item-[ModuleContext:ModuleId]' ng-controller="ItemController"
ng-init="init([ModuleContext:ModuleId],'[ModuleContext:ModuleName]','[ModuleContext:EditMode]')">
...
</div>
Also on your View.html, bootstrap your angular app:
<script type="text/javascript">
angular.element(document).ready(function () {
var moduleContainer = document.getElementById('dnnuclear-item-[ModuleContext:ModuleId]');
angular.bootstrap(moduleContainer, ["dnnuclear.ItemApp"]);
});
</script>
Then in your .js file, define the angular module and controller with the init method implementation:
var dnnuclear = dnnuclear || {};
dnnuclear.ItemApp = angular.module("dnnuclear.ItemApp", ['ngDialog']);
dnnuclear.ItemApp.controller("ItemController", function ($scope, $window, $log, ngDialog, dnnServiceClient) {
...
$scope.init = function (moduleId, moduleName, editable) {
$scope.ModuleId = moduleId;
$scope.EditMode = editable;
dnnServiceClient.init(moduleId, moduleName);
$scope.getAll();
}
}
If you need to pass more information to the module than a few variables, I would recommend changing this approach. I have more on this in my DNNHero.com tutorial, Advanced Angular Concepts.
You should try the AngularDNN module. It will probably save your time. It can be found here:
http://store.dnnsoftware.com/home/product-details/angulardnn

AngularJS - Running a function once on load

I am trying to run an $http function when my AngularJS application first loads.
This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...
I currently have the function that I want to run first like this:
app.run(function() {
$http.get('link').success(function(data) {
// success function. The data that I get from this HTTP call will be saved to a service.
}).error(function(error) {
});
});
However, sometimes the controller will load before the http call finishes.
The problem
Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)
The cure
But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution
var $http_injected = angular.injector(["ng"]).get("$http");
The act
First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this
<body>
<div ng-controller="Controller1">
<b>Controller1</b>
{{text}}
{{setting.data.name}}
</div>
<hr>
<div ng-controller="Controller2">
<b>Controller2</b>
{{text}}
{{setting.data.name}}
</div>
<script>
//define preloader
var $http_injected = angular.injector(["ng"]).get("$http");
$http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {
//define app
angular.module('app', []);
//define test controllers
//note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
angular.module('app').controller('Controller1', function($scope, AppSetting) {
$scope.text = 'controller1 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
angular.module('app').controller('Controller2', function($scope, AppSetting) {
$scope.text = 'controller2 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
//define test services, note we assign it here, it's possible
//because of javascript awesomeness (closure)
angular.module('app').service('AppSetting', function() {
this.setting = successResponse;
});
//bootstrap app, we cannot use ng-app, as it loads app instantly
//but we bootstrap it manually when you settings come
angular.bootstrap(document.body, ['app']);
});
</script>
</body>
Plunker demo
You can do this when you configure your routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
data: ['$http',
function($http)
{
return $http.get('/api/data').then(
function success(response) { return response.data.rows[0]; },
function error(reason) { return false; }
);
}
]
}
});
}]);
Similar question:
AngularJS - routeProvider resolve calling a service method
AngularJS: $routeProvider when resolve $http returns response obj instead of my obj
Heres a plunkr I found using a service, which is what I would recommend.
http://plnkr.co/edit/XKGC1h?p=info

Compiling content that contains custom attributes and returned from an $http service

I am working on an angular application that relies heavily on the client-side and renders most of the app's components through a RESTful API. One of which, is a navigation component which is stored in a JSON file, and is comprised of a list of links that use the ui-router syntax to navigate between states.
So far, I've managed to write a service that builds the nav from a json. Then, using a controller, I'm rendering it to the view using angular's sanitize service. The part I'm stuck at is, the result links appear as hard-coded strings that aren't clickable.
From different threads I read, I assume it's because they aren't compiled and just thrown to the view, but I'm unable to get them to compile / work.
I've seen similar threads such as here and here but they all relay on creating a custom directive for it. I need to render and compile dynamic html (ui-sref links in specific) that are returned from an $http service.
JSFiddle
<div ng-controller="TopNavController as TopNavCtrl">
Output Navigation :
<div ng-bind-html="topnavCtrl.navbar"></div>
</div>
<!-- json file contains something similar to this :
<ul>
<li><a ui-sref="catpage({category: 'products', subcategory: 'whale toys'})" </a>Whale Toys</li>
<li><a ui-sref="catpage({category: 'products', subcategory: 'sharks toys'})"">Shark Toys</a></li>
</ul>
-->
var myApp = angular.module('myApp',[]);
myApp.controller('TopNavController', ['NavbarFactory', '$sce', function(NavbarFactory, $sce) {
var self = this;
self.navbar ="";
NavbarFactory.getnav().then(function(data) {
self.navbar = $sce.trustAsHtml(data);
});
}])
.factory('NavbarFactory', ['$http', function($http) {
return {
getnav: function() {
return $http.get('/path/myjson').then(function(answer) {
var result = answer.data;
return result
},
function(error) {
console.log('Failed');
});
}
}
}]);

Resources