AngularJS route helper - angularjs

Any ideas how to build solution, that helps generate url in view in more convenient way, instead of hard-coding like that:
<a ng-href="#/Book/{{item.bookId}}/ch/{{item.id}}">Chapter {{item.id}}</a>
I want to use:
<a chapters="[item.bookId, item.id]">Chapter {{item.id}}</a>
So it checks routes and generates for each route specific directive.
I'm interested in the most generic solution as possible.

I would highly recommend you make use of ui-router, and its $stateProvider.
var app = angular.module('yourModuleName', ['ui.router']);
app.config(function ($stateProvider) {
$stateProvider
.state('book', {
url: '/Book/:bookId'
})
.state('book.chapter', {
url: '/ch/:id'
});
});
<a ui-sref="book.chapter({bookId: item.bookId, id: item.id})">Chapter {{item.id}}</a>
Something along those lines should do the trick. I'm not at all familiar with the other parameters of your application, but building dynamic URL's with passed in parameters to match up with a $state is a breeze.
ui-router: https://github.com/angular-ui/ui-router
ui-sref (directive): https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref

First of all you can use Angular-UI
Angular-UI/UI-Router
The main idea in there is to have states because you have single page app and everything are just state who renders in single place. No refresh nothing.
When integrating it you can create
$stateProvider
.state("bookPreview", {
url: "/book/:id/:itemId",
controller: "BookPreviewCtrl",
templateUrl: 'sb/add-cpe-template.tpl.html'
});
In your html you can do the following thing:
<button ng-click="view(item.bookId, item.id)">Chapter {{item.id}}</button>
or something like that, you can assign ng-click on hyperlinks as well.
The java script view function is:(but before you must inject $
controller("BookSelectionCtrl",function($scope,$state){
//this will rewrite the url , go to the state and load the template(view).
$scope.view=function(bookId,itemId){
$state.go("bookPreview",{id:bookId,itemId:itemId}) //there is a third object representing options, which are optional and you can check them on doc site
}
})
controller("BookPreviewCtrl",function($scope,$stateParams){
//in this new ctrl of the new view you can now do what ever you want with these params
$scope.bookId = $stateParams.id;
$scope.itemId = $stateParams.itemId;
})

You need to loop over all routes and build directives dynamically, here is a start http://plnkr.co/edit/d592a58CMan5BVKQYSAy?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$route) {
var keys = [];
for(var k in $route.routes) keys.push(k);
$scope.name = keys;
for (var i=0; i<keys.length; ++i) {
app.directive('SomethingDynamic', function() {
return {
restrict: 'A',
replace: true,
template: '....',
};
});
}
});
app.config(function ($routeProvider) {
$routeProvider.
when('/complex/:name/:color/:number/extra', {
templateUrl: "404.html",
name:'complex'
}).
when('/objects', {
templateUrl: "404.html",
name:'list'
}).
when('/object/detail/:id', {
templateUrl: "404.html",
name:'detail'
});
});

Or you can create directive (jsbin):
View:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="app">
<div ng-controller="mainCtrl">
<book-link book="book" ng-repeat="book in books"></book-link>
</div>
</body>
</html>
JS:
var app = angular.module("app",[])
.controller("mainCtrl", function($scope){
var books = [
{bookId : "book1", id:"1" },
{bookId : "book1", id:"2" },
{bookId : "book1", id:"3" }
];
$scope.books = books;
})
.directive("bookLink",function(){
return {
restrict:"E",
scope:{book:'='},
template:"<a href='/book/{{book.bookId}}/ch/{{book.id}}'>Chapter {{book.id}}</a><br/>"
};
});

Related

Angular-UI Router Nested Views Not Working for some Mobile browsers

I am using angular ui-router for an application, but for some reason the nested views are loaded only for desktop browsers and Chrome for android but not for other mobile browsers such as chrome for iPhone, Samsung Browser for android, Safari for iPhone.I have been searching but I could not find any reasons why it behaves like that, I don't know if maybe the configuration of my "states" and templates are not correct.
and this is my code:
$urlRouterProvider.otherwise(function ($injector) {
var $state = $injector.get('$state');
var $rootScope = $injector.get('$rootScope');
var $stateParams = $injector.get('$stateParams');
if (typeof $stateParams.token == 'undefined') {
$rootScope.errorList = [];
$rootScope.errorList.push("Token is invalid");
$state.go('error');
}
});
$stateProvider
.state('index',
{
url: '/:token/',
views: {
'': {
templateUrl: 'AngularJS/Templates/indexView.html',
controller: 'candidateController as candCtrl'
},
'sectioncandidate#index': {
templateUrl: 'AngularJS/Templates/candidatesView.html'
}
}
})
.state('error',
{
url: '/error',
templateUrl: 'AngularJS/Templates/errorView.html',
controller: 'errorController as errorCtrl'
})
.state('index.details', {
url: 'details',
views: {
'sectioncandidate#index': {
templateUrl: 'AngularJS/Templates/candidateView.html'
}
}
});
The templates have the following hierarchy:
index.cshtml
<div class="main-container" ui-view></div>
indexView.html
<h3 class="header-title">This is the header from the parent view</h3>
<div class="body-content">
<div ui-view="sectioncandidate"></div>
</div>
candidatesView.html
<h1>This is the nested view!!!!</h1>
And i am loading the following libraries
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-route.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-aria.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-animate.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-messages.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.9/angular-material.js"></script>
<script src="~/AngularJS/Modules/app.js"></script>
<script src="~/AngularJS/Services/applicationService.js"></script>
<script src="~/AngularJS/Controllers/candidateContoller.js"></script>
<script src="~/AngularJS/Controllers/errorController.js"></script>
I would really appreciate any help.
You Can Try On When Method
var app = angular.module('Appname',['ngRoute']);
app.config(function($routeProvider){
$routeProvider.when('/',{
templateUrl :"1st Path Of File",
controller :"FirstController"
})
.when('/AnchortagIdHere',{
templateUrl :"2nd Path Of File",
controller :"2ndController"
})
.otherwise('/',{
templateUrl :"1st Path Of File",
controller :"FirstController"
});
});
Well, after the application was dissected line by line I could realize that the issue with the nested views were not exactly because of the ui-router component, the problem was that the controller used by the nested views was using an angular material dialog. The way these angular material dialog link a controller and a template, was creating a conflict for some mobile browsers, therefore the nested, named views were not displayed.
This fixed the problem:
Before I was using a code like this to pen a dialog: (it is supposed to work with es6)
this.$mdDialog.show({
targetEvent: event,
templateUrl: 'path/mytemplate.html',
controller: () => this,
controllerAs: 'ctrl'
});
I changed for something like this: (the way to link a controller Without es6)
var self = this;
this.showTabDialog = function(ev) {
$mdDialog.show({
controller: function () {
return self;
},
controllerAs: 'ctrl',
templateUrl: 'tabDialog.tmpl.html',
});
};
hope it could be useful for someone.

Sub Domain Routing in AngularJS

Hello i am new to AngularJS, i have done my research and i know redirecting to a sub domain will cause the page to refresh. What i want to know is how to check what the sub domain is when a link is clicked and load the right view and controller.
I use wildcard sub domains but i basically have three of them right now:
administrator.example.com
manager.example.com
employee.example.com
Each sub-domain should redirect you to their respective dashboard view and controllers.
The main url however example.com shall direct you to the website itself.
I have set-up my states like this:
app
app.administrator
app.administrator.dashboard
app.manager
app.manager.dashboard
app.employee
app.employee.dashboard
Here i am going to give you standard solution to your problem. You should create a separate application for each subdomain. I have created a sample application to demonstrate the flow.
Index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script data-require="jquery#2.0.0" data-semver="2.0.0" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<link data-require="bootstrap#3.3.5" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<script data-require="bootstrap#3.3.5" data-semver="3.3.5" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.3/angular.js" data-semver="1.4.3"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<script src="app.js"></script>
<script src="app.manager.js"></script>
</head>
<body>
<load-application></load-application>
</body>
</html>
app.js
/**
* #name app
*
* Lets create angular application module
* if second parameter is passed with enpty array [] or with dependecies , is * called angular module setter
*
* angular.module('app', []); Setter
* angular.module('app'); Getter
*/
angular
.module('app', ['appLoader']);
/**
* app.administrator application for aministrator subdomain
*/
angular
.module('app.administrator', ['ui.router']);
/**
* app.employee application for employee subdomain
*/
angular
.module('app.employee', ['ui.router']);
/**
* Lets create a application loader
* This component is going to load sub domain specific application
*/
angular
.module('appLoader', ['app.administrator', 'app.manager', 'app.employee'])
.directive("loadApplication", ['$location', function($location) {
function getDomain() {
console.log($location.host());
//get domain name here
return 'manager';
}
return {
restrict: 'E',
controller: function($scope, $element, $attrs, $transclude) {},
template: function() {
var domainName = getDomain(),
templateName = '';
switch (domainName) {
case 'manager':
templateName = '<app-manager></app-manager>';
break;
case 'employee':
templateName = '<app-employee></app-employee>';
break;
case 'administrator':
templateName = '<app-administrator></app-administrator>';
break;
}
return templateName;
},
link: function($scope, element, attrs) {
console.info('loader application');
}
};
}]);
angular
.module('app.administrator')
.directive("appAdministrator", ['$location', function($location) {
return {
restrict: 'E',
template: '<h2>{{applicationName}}</h2>',
link: function($scope, element, attrs) {
$scope.applicationName = 'Application Administrator';
}
};
}]);
angular
.module('app.employee')
.directive("appEmployee", ['$location', function($location) {
return {
restrict: 'E',
template: '<h2>{{applicationName}}</h2>',
link: function($scope, element, attrs) {
$scope.applicationName = 'Application Employee';
}
};
}]);
app.manager.js
angular
.module('app.manager', ['ui.router']);
angular
.module('app.manager')
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('index', {
url: '/',
template: '<ul><li><a ng-href="#" ui-sref="demopage1">Demo page 1</a></li><li><a ng-href="#" ui-sref="demopage2">Demo page 2</a></li></ul>'
})
.state('demopage1', {
url: '/demopage1',
template: '<ul><li><a ng-href="#" ui-sref="demopage1">Demo page 1</a></li></ul>'
})
.state('demopage2', {
url: '/demopage2',
template: '<ul><li><a ng-href="#" ui-sref="demopage1">Demo page 1</a></li></ul>'
})
});
angular
.module('app.manager')
.directive("appManager", ['$location', function($location) {
return {
restrict: 'E',
template: '<h2>{{applicationName}}</h2><div ui-view></div>',
link: function($scope, element, attrs) {
$scope.applicationName = 'Application Manager';
}
};
}]);
Working plunker is here
I have created 3 applications for three domains , each application works independently. I have implemented the UI routes in app.manager application. You can implement the same in rest applications.
Lets me know if you have any concern ?
If you haven't started using the routing module called ui.router in your angular-based app, I suggest you read it here. I believe it is much more powerful than the ngRoute module native in Angular 1.x.
From my understanding your subdomain, I think you can arrange them as nested domains (or to be more precise, states) under 'app'. If the root url of your 'app' is something like '/main/', then the subdomains will inherit that root url and take the form such as '/main/administrator' and '/main/administrator/dashboard'
Each state (including nested states) can have its own controller, in which you can use the $state service in ui.router to get the state name (or subdomain name as you said) by calling: $state.$current.name
I hope this helps
As an example from my previous work. Here the main app has the url '/webapp', and the rest of the states are nested states that have their url started with '/webapp', e.g. for the state 'home.blogs', its url is '/webapp/blogs'. If it's still confusing, I suggest you spend some time reading the documentation on ui.router at Github.
angular
.module('blogFrontendApp', [
'ui.router'
])
config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/webapp/blogs');
$stateProvider.state('home', {
// abstract: true,
url: '/webapp',
views: {
'' : {
templateUrl: 'views/main.html'
},
'rightPanel#home' : {
templateUrl: 'views/rightPanel.html'
},
'mainContent#home' : {
templateUrl: 'views/mainContent.html'
}
}
})
.state('home.blogs', {
//to be shown in mainContent#home
url: '/blogs',
templateUrl: 'views/home_blogs.html'
})
.state('home.singleBlog', {
url: '/blog/:blogId/:blogTitle',
templateUrl: 'views/single_blog.html',
controller: 'blogArticleController as currentBlogView',
resolve: {
BlogService : 'BlogService',
blogObj : function(BlogService, $stateParams) {
return BlogService.getBlogById($stateParams.blogId);
}
}
})
.state('home.blogListInCategory', {
url: '/blogs/category/:categoryId',
templateUrl: 'views/blog_list_in_category.html',
controller: 'blogListController as blogListView'
})
.state('home.publishBlog', {
url: '/blogs/:categoryId/new',
templateUrl: 'views/publish_blog.html',
controller: 'blogListController as writeBlogView'
})
}

Get an element from a template that is configured as ng-view template?

var shop = angular.module("shopStore",['ngRoute']);
shop.controller('productsList',function($scope){
$scope.stock = [
{type: "motherboard",model: 'AM1I',company: 'MSI'},
{type: "motherboard",model: ' A88XM-PLUS/CSM FM2', company: 'MSI'}
];
});
shop.config(function ($routeProvider){
$routeProvider.
when('/', {
controller: 'productsList',
templateUrl: "partials/home.html"
}).
when('/products', {
controller: 'productsList',
templateUrl: "partials/products.html"
}).
when('/cart', {
controller: 'productsList',
templateUrl: 'partials/cart.html'
})
});
var x = document.querySelector('#elem');
console.log(x);
// Doesn't work, it doesn't work with all the others.
the products arrangement doesn't matter, I have another file with all the information,
it is just to let you know the structure
<b><div id="elem"></div></b>
<div id="stock" ng-repeat="products in stock" ondrag="part.drag(this)">
<div class="image">
<a href={{products.link}}>
<img ng-src={{products.picture}} title{{products.company}} width="70px">
</a>
</div>
<div class="type">{{products.type}}</div>
<div class="company">{{products.company}}</div>
<div class="model">{{products.model}}</div>
<div class="button1"><button ngclick="add(product)">Add</button></div>
<div class="buttonInput">
<input type="button" onclick='Shoping.remove(this)' value="X">
</div>
</div>
When I try to call any of the elements in the partials html with document by ID or querySelector it doesn't work; what should I write to get the element from a ng-view template?
The js is on the top and the template html is at the bottom it is for regular usage, not in angular js
The following adds dynamically created variables to the $scope object, making the variables available to the controller. The variables are added to the main app page by the ng-view tag.
AngularJS
var app = angular.module('myApp', ['ngRoute', 'ngResource']);
app.config(function ($routeProvider) {
$routeProvider.when('/', {
controller: 'productsList',
templateUrl: "partials/home.html"
}).when('/products', {
controller: 'productsList',
templateUrl: "partials/products.html"
}).when('/cart', {
controller: 'productsList',
templateUrl: 'partials/cart.html'
})
});
app.controller('MainController', ['$scope', function ($scope) {
$scope.stock1 =[ {type: "motherboard",model: 'AM1I',company: 'MSI'},
{ type: "motherboard",model: ' A88XM-PLUS/CSM FM2', company: 'MSI' }
] ;
//The following is where the magic is realized!
//**var 'directiveElement' locates the dynamically created variable.
//Id 'item0' is the first span Id dynamically created by the stock-scope object's
//'template' attribute**
var directiveElement = angular.element(document.getElementById('item0'));
});
}]);
app.directive('stockScope', function () {//name
return {
restrict: 'E', //'E' matches only names in elements
replace: true, //will replace the stock1 $scope object declared above once
//it is matched to it, in this case we are using the
//html '<stock-scope stock="stock1"></stock-scope>'
//to match it to the directive parameter of 'stock' declared below
scope: {
stock: '='
},
template: '<p><span id="item{{index}}" ng:repeat="(index, item) in stock">'
+ '{{item.type}}, {{item.model}}, {{item.company}} <br /></span></p>'
};
});
HTML
<!-- add the following html to the partials/products.html -->
<stock-scope stock="stock1"></stock-scope>
Also, make the starting app html page to include the ng-view tag,
<html ng-app="myApp">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-resource.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<div ng-view></div>
</body>
</html>
Research Links
https://docs.angularjs.org/guide/directive
How to use `replace` of directive definition?

How do I correct this use of ui-router multiple named views

I'm trying to setup a page that has no parent template but contains multiple named views using angular ui-router.
A plunkr is here
There is a simple html setup:
<body>
<div ng-app="thing">
<h1>Multiple Views</h1>
<div ui-view="oneThing"></div>
<div ui-view="twoThing"></div>
<div ui-view="threeThing"></div>
<script id="one_thing.html" type="text/ng-template">
<p>one: {{text}}</p>
</script>
<script id="two_thing.html" type="text/ng-template">
<p>two: {{text}}</p>
</script>
<script id="three_thing.html" type="text/ng-template">
<p>three: {{text}}</p>
</script>
</div>
</body>
I expect this to mean I have three views and three templates that the 'thing' app can see
Then the JS setup is:
'use strict';
var thing = angular.module("thing", ['ui.router']);
thing.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('things', {
url: '/things',
views: {
'oneThing': {
templateUrl:'one_thing.html',
controller: 'oneCtrl'
},
'twoThing': {
templateUrl:'two_thing.html',
controller: 'twoCtrl'
},
'threeThing': {
templateUrl:'three_thing.html',
controller: 'threeCtrl'
}
}
});
$urlRouterProvider.when('/', 'things');
});
thing.controller('oneCtrl', function($scope) {
$scope.text = 'boom';
});
thing.controller('twoCtrl', function($scope) {
$scope.text = 'bang';
});
thing.controller('threeCtrl', function($scope) {
$scope.text = 'wallop';
});
I thought this meant always load 'things' as the route and within that state load three views each pointing to a different template and controller. But instead I get a blank page...
Have I misunderstood how this works or how to write it (or, worse, both!!)?
The default url is not /
So $urlRouterProvider.when('/', 'things'); is not being triggered.
Change to
$urlRouterProvider.when('', 'things');
and your plunkr works.

Does AngularJS have dynamic routing?

Does angular support dynamic routing at all?
Maybe some trick like this:
$routeProvider.when('/:ctrl/:action',
getRoute($routeParams.ctrl,$routeParams.action))
function getRoute(ctrl, action){
return {
templateUrl: ctrl+"-"+action+".html"
controller: 'myCtrl'
}
}
Please help me, I need to get templateUrl based out of routeParams
This is a late answer but I came across this problem myself, but it turns out that the solution by Dan conflicts with ngAnimate classes on the ngView directive, and the view is shown but the ng-leave animation will immediately be applied and hide the view opened with his dynamic routing.
I found the perfect solution here, and it's available in 1.1.5 +
In the $routeProvider, the templateUrl value can be a function, and is passed the route parameters:
app.config(function ($routeProvider) {
$routeProvider
.when('/:page', {
templateUrl: function(routeParams){
return '/partials/'+routeParams.page+'.html';
}
})
});
Though the controller can't be given as a function so my solution is to give it in the template html as per usual with ng-controller="HomeCtrl".
Using this solution we can route by convention in Angular.
I hope this helps others who weren't keen on manually adding every route to the routeProvider.
You want to bring it down to the controller level.
In this example, I am overriding entire pages as well as partials by subdomain:
app.js
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/', {
template: 'home'
});
$routeProvider.when('/contact', {
template: 'contact'
});
$routeProvider.otherwise({redirectTo: '/'});
}])
controllers.js
controller('AppController', ['$scope','Views', function($scope, Views) {
$scope.$on("$routeChangeSuccess",function( $currentRoute, $previousRoute ){
$scope.page = Views.returnView();
});
$scope.returnView = function(partial){
return Views.returnView(partial);
}
}])
services.js
factory('Views', function($location,$route,$routeParams,objExistsFilter) {
var viewsService = {};
var views = {
subdomain1:{
'home':'/views/subdomain1/home.html'
},
subdomain2:{
},
'global.header':'/views/global.header.html',
'global.footer':'/views/global.footer.html',
'home':'/views/home.html',
'home.carousel':'/views/home.carousel.html',
'contact':'/views/contact.html',
};
viewsService.returnView = function(partial) {
var y = (typeof partial === 'undefined')?$route.current.template:partial;
var x = $location.host().split(".");
return (x.length>2)?(objExistsFilter(views[x[0]][y]))?views[x[0]][y]:views[y]:views[y];
};
viewsService.returnViews = function() {
return views;
};
return viewsService;
}).
filters.js
filter('objExists', function () {
return function (property) {
try {
return property;
} catch (err) {
return null
}
};
});
index.html
<!doctype html>
<html lang="en" ng-controller="AppController">
<body>
<ng-include src="returnView('global.header')"></ng-include>
<ng-include src="page"></ng-include>
<ng-include src="returnView('global.footer')"></ng-include>
</body>
</html>

Resources