AngularJS loading partial views - angularjs

I'm developing a modular application with angularJS.
There is a menu that allows navigation through multiple views. (i'm using ngRoute)
Each view is divided in few parts and each part should load an independent module (lets call it "modules" atm). These modules will retrieve dynamic data using an api and these modules could be used in several views.
Which is the best way to do this? Use custom directives with a template and controller for each?

I would create the modules with a "module.(module #)" so you can separate all js files. Something like this since you are using ngroute:
MyApp.controller('module.one', function ($scope, $http, $routeParams, moduleOneResource) {...logic... }
Create a factory for each module:
angular.module('module.one').factory('moduleOneResource', ['$resource', function ($resource) {
return $resource('/api_root/module/:module_id', {} {
'save': {
method: 'POST',
headers: {"Content-Type": "application/json"},
'get': {
method: 'GET',
headers: {"Content-Type": "application/json"},
}
}
});
}]);
And the config for the module:
angular.module('module.one', []).config(['$routeProvider',
function($routeProvider) {
$routeProvider.when('/module/one/new', {templateUrl: 'partials/moduleOne/new.html', controller: 'ModuleOneCtrl'});
$routeProvider.when('/module/one/list_all', {templateUrl: 'partials/moduleOne/list.html', controller: 'ModuleOneCtrl'});
}]);
Then just keep creating each one of these files for each modules, should be 3 files per module... you can include more than one controller for example in one controller file if you want to for one module, same with factories.
angular.module('module.two', []).config([ .... config module for each module with url routes and html source , etc...

You could use ng-include for this but I would say its better to use ui-router instead of ngRoute. ui-router allows you to use multiple named views and nested views which could be what you want i think. For example
<body ng-app="myApp">
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
</body>
in config
var mypApp = angular.module("myApp",[ui-router]);
myApp.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('empty', {
url:'/',
views: {
'header': {
templateUrl: 'apps/header.html',
controller: headController
},
'content': {
templateUrl: 'apps/content.html'.
controller: contentController
},
'footer': {
templateUrl : 'apps/footer.html',
controller: footerController
}
}
})
.state('test',{
url:'/test',
views: {
'header': {
templateUrl: 'apps/headertest.html'
controller: headtTestController
},
'content': {
templateUrl: 'apps/contenttest.html',
controller: contenTesttController
},
'footer': {
templateUrl : 'apps/footertest.html',
controller: footerTestController
}
}
})
}]);
This is a basic example as to how app is divided into multiple ui-views and you could devide your app similarly with each view having a controller.

Related

Modularize AngularJS App Using UI-Router

I'm trying to modularize a large AngularJS app (1.4.8) that is using UI-Router.
The current structure is like this:
transactionApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function ($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('root', {
url: '/transaction',
views: {
'header': {
templateUrl: '/Partials/Shells/Header/header-partial.html',
controller: 'headerController',
resolve: {
headerItems: ['$http', function ($http) {
var data = {};
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
if (dataForLayout.IsLoggedIn) {
return $http({
method: 'POST',
url: '/myaccount/GetHeaderAccountInfo',
data: data
});
}
}]
}
},
'footer': {
templateUrl: '/Partials/Shells/Footer/footer-partial.html',
controller: 'footerController'
},
'mainPanel': {
templateUrl: '/Partials/Shells/Transaction/transaction-partial.html',
controller: 'mainPanelController'
},
'successPanel': {
templateUrl: '/Partials/Shells/Transaction/transaction-success-panel.html',
controller: 'successPanelController'
}
}
});
$urlRouterProvider.otherwise('/transaction');
// Enable HTML5Mode for #-urless
$locationProvider.html5Mode(true);
}]);
The part I want to modularize is "mainPanel." The mainPanel consists of step 1, step 2, step 3, and the final review/submit page. I'd also like to separate pop-up modals if possible/beneficial. The steps, review page, and modals are dependent on each other.
The URL has to stay "/transaction" no matter which step the user is in.
What I'm envisioning is having the parent as "mainPanel" that connects the sub modules and having sub modules that are "step 1," step 2," and so on.
I've read some articles, but can't seem to find one that talks about further modularizing a module.

Angular UI-Router childs

I have an app.config with UI-Router. It has a login page with it's controller, recoverLogin and I want to put a template with footer, header and more stuff with new states that could be loaded into the template (in an especificplace).
My module is:
var app = angular.module("app", [
"ui.router",
"pascalprecht.translate"
]);
My routes are;
app.config(function($stateProvider, $urlRouterProvider)
{
$stateProvider
.state("login", {
url: "/login",
templateUrl: "views/accessControl/login.html",
controller: "loginCtrl"
});
$stateProvider
.state("recoverLogin", {
url: "/recoverLogin",
templateUrl: "views/accessControl/recoverLogin.html",
controller: "recoverLoginCtrl"
});
$stateProvider
.state("template", {
url: "/template",
templateUrl: "views/templates/template.html",
controller: "templateCtrl"
})
.state("template.dashboard", {
url: "/dashboard",
templateUrl: "views/dashboard/dashboard.html",
controller: "dashboardCtrl"
})
$urlRouterProvider.otherwise("login");
})
I have in my index <ui-view></ui-view> for the place of the loadings and another <ui-view></ui-view> in template.html int he place where I want to load more stuff like dashboard.html, but this doesn't works. it loads dashboard.html without the template created in template.html. I have founded lot of documentation that doesn´t works for me. Any Idea?
Here there are a plunker example of the idea: https://plnkr.co/edit/ZsGZjDKOBTIXFpPtXasN?p=preview
There is updated plunker and working plunker.
The template of the mainTemplate state is now lookin like this:
place for main:
<div ui-view="main"></div>
place for other:
<div ui-view="other"></div>
so it has two (could be more) places for more stuff. And this is the state redefined:
.state("mainTemplate.dashboard", {
name: "main",
url: "/dashboard",
views: {
'main' : {
templateUrl: "dashboard.html",
controller: "dashboardCtrl"
},
'other' : {
template: "<h2>other view</h2>",
}
}
});
What we can see is views : {} object being used to defined multiple content for more targets. Read about that more details here:
Angular UI Router - Nested States with multiple layouts
Nested states or views for layout with leftbar in ui-router?
play or observe the changes here

Separate common parts of controller into a common controller

I'm developing an web app using AngularJS with uiRouter. I developed my route configuration as follows:
(function () {
'use strict';
var module = angular.module('app', ['ngMaterial', 'ui.router']);
function Config($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('Home', {
url: '/',
templateUrl: 'Partials/homeview.html',
controller: 'homeCtrl'
}).state('Table', {
url: '/tableview',
templateUrl: 'Partials/tableview.html',
controller: 'tableCtrl'
}).state('List', {
url: '/listview',
templateUrl: 'Partials/listview.html',
controller: 'listCtrl'
}).state('New', {
url: '/new',
templateUrl: 'Partials/new.html',
controller: 'newCtrl'
}).state('Edit', {
url: '/edit/:index',
templateUrl: 'Partials/edit.html',
controller: 'editCtrl'
});
}
Config.$inject = ["$urlRouterProvider", "$stateProvider"];
module.config(Config);
}());
The thing in some controller passed to the view the code is duplicated, so I would like to know if there is a way to pass 2 controllers to the view at the same time or if there is a way to create a separate file with that specific part of the duplicated controller and pass it as Dependency Injection in the desired controllers.
You can't have two controllers linked to a uiRouter route. But you could certainly make a service or factory that includes your universal functionality. (See angular.service vs angular.factory for more research.)
var app = angular.module('app',[])
app.service('myFunctions',function() {
this.addNumbers = function(a,b) {
// calculate some stuff
return a+b;
}
}
app.controller('myController',function(myFunctions){
myFunctions.addNumbers(2,2); // 4
})

How to do A/B testing with AngularJS templates?

I'm using ng-boilerplate and have to add the possibility to use different templates in production, based on the user configuration.
.config(function config( $stateProvider ) {
$stateProvider.state( 'demo', {
url: '/demo',
views: {
"main": {
controller: 'DemoCtrl',
templateUrl: 'demo/demo.tpl.html'
}
}
});
})
My current idea is to make the templateUrl dynamic
templateUrl: 'demo/demo'+userService.getTemplate()+'.tpl.html'
and having multiple template files, like:
demo.tpl.html (default)
demo.b.tpl.html (version b)
demo.c.tpl.html (version c)
while the userService function does provide the template version to use, e.g. ".b"
Do you agree? Is there maybe a better/easier approach to this problem?
AngularJS standard $routeProvider can accept function for templateUrl. But you can't inject services into this function.
ui-router has templateProvider parameter into which you can inject what you want and you should return something like this for remote template case:
$stateProvider.state('demo', {
templateProvider: function ($http, $templateCache, $stateParams, userService) {
var url = 'demo/demo' + userService.getTemplate() + '.tpl.html';
return $http.get(url, { cache: $templateCache }).then(function (response) {
return response.data;
});
}
})
I will not keep it in service, because service will be a part of js file. Which will be static (under normal condition)
This is how I will do it, In html file I will put
window.abConfig = "defaultVersion";
In app.js I will put
.config(function config( $stateProvider ) {
$stateProvider.state( 'demo', {
url: '/demo',
views: {
"main": {
controller: 'DemoCtrl',
templateUrl: function() {
return 'demo/demo' + window.abConfig + '.tpl.html';
}
}
}
});
})
Its kind of Hacky way, but it gives me flexibility to decide which version to display to user at server level. I might require to write logic before user download the content based on user's previous activity, which I can not do from client side javascript.
This can be achieved using standard angular, you just have to look at it from another angle!
I would suggest using the $templateCache. When you load the app you can pre-populate the $template cache with the selected version of the user templates.
You can do something like
$templateCache.put("page-header.html", '<h1>MyAwesomeStartup</h1><h2>Buy now!?!?!</h2>');
Also if your not opposed to the idea, you can place the templates into the page using the script tag syntax, where the id == the templateURL you use in your $routeProvider.
<script type="text/ng-template" id="page-header.html">
<h1>MyAwesomeStartup</h1><h2>Buy now!?!?!</h2>
</script>
And ng will load it directly from the script tag.
I've a different way based on the same principle
Except you don't have to actually request the view yourself with $http.
So you can let ui-router handle that part.
Which is easier when you have a complex view architecture.
.state('public.index', {
url: '/',
views: {
"": {
template: '<div ui-view="abTestDummyView"></div>',
controller: ['landing', '$http', function(landing, $http) {
alert('Showing AB Test Landing #' + landing);
// increment landing stats
$http.get('http://stats.domain.com', {landing: landing});
}],
controllerAs: 'landingCtrl',
},
"abTestDummyView#public.index": {
templateProvider: ['landing', function(landing) {
// inject a view based on its name
return "<div ui-view=\"ab" + landing + "\"></div>";
}]
},
"ab1#public.index": {
template: "INJECTED AB1"
// replace by templateUrl: "/real path/"
},
"ab2#public.index": {
template: "INJECTED AB2"
// replace by templateUrl: "/real path/"
}
},
resolve: {
landing: function() {
return Math.floor((Math.random() * 2) + 1);
}
}
})

Routing in angularjs for multiple controllers?

I'm trying to build a view - I've set up two controllers to practice, one's HeaderCtrl, with some data in it (site title, header background, etc), the other should have the main content of the page - MainCtrl.
When defining the route, I'm doing like so:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
})
This works perfectly fine, but what I would want is to specify multiple parameters to this, something like this:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'HeaderCtrl',
templateUrl: 'modules/header.html'
},
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
})
This doesn't work so I'm guessing it's not the way to do it. What I'm actually asking - can you specify multiple controllers in $routeProvider ? Or what would be the correct way to build this view ?
My approach to this problem would be to have two directives - header and main which reference the corresponding templates.
For Example:
app.directive('header', function () {
return {
restrict: 'A',
replace: true,
templateUrl:'templates/header.html'
}
})
Then you can have a page that contains the directives - index.html.
<div header></div>
<div main></div>
Then have one controller that routes to your index.html
You can also encapsulate the header and main in separate header and main controllers if you want to deal with them separately.
e.g.
<div ng-controller="HeaderCtrl">
<div header></div>
</div>
<div ng-controller="MainCtrl">
<div main></div>
</div>
or with the templates themselves
What you might want to do is place your HeaderCtrl outside of ng-view and then have MainCtrl mapped to your index route (ie. '/'). If you needed to have multple controllers mapped to your index route, you can assign them within the template that you have mapped to that route. Here is what that would look like:
index.html
<html>
<body ng-app='yourApp'>
<div id="header" ng-controller="HeaderCtrl"></div>
<div ng-view></div>
</body>
</html>
app.js
angular.module('mainApp', []).
config(function ($routeProvider) {
$routeProvider.when('/', {
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
});
dashboard.html
<div ng-controller="SomeCtrl"></div>
<div ng-controller="SomeOtherCtrl"></div>
...or, if you really wanted to get creative, you could include ui-router from the AngularUI folks https://github.com/angular-ui/ui-router This would allow you to have multiple "views" and map them to your routes.
You should use angular's ui-router : https://github.com/angular-ui/ui-router, you can specify a controller and template for each "element" of your page :
myApp.config(function($stateProvider) {
$stateProvider
.state('index', {
url: "",
views: {
"viewA": { template: "index.viewA" },
"viewB": { template: "index.viewB" }
}
})
.state('route1', {
url: "/route1",
views: {
"viewA": { template: "route1.viewA" },
"viewB": { template: "route1.viewB" }
}
})
.state('route2', {
url: "/route2",
views: {
"viewA": { template: "route2.viewA" },
"viewB": { template: "route2.viewB" }
}
})
});
I don't think you can specify multiple controllers. I think what you're looking for is something like an 'index.html' template that refers to your header and dashboard:
<div id='header' ng:controller='HeaderCtrl' />
<div id='main' ng:controller='MainCtrl' />
To actually fill in with the right templates, I would use a directive. I think this is one of the most powerful parts of angular. You can create a header directive that you can reuse on all your pages.
Try this it should work
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'HeaderCtrl',
templateUrl: 'modules/header.html'
}).when('', //route here in the empty quotes
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
});
});

Resources