AngularJS multilanguage routing - angularjs

I have an AngularJS webapp. I´m changing the application so the URLs can be multilanguage (for SEO indexing purposes).
I´m setting up my app.js, like this:
$routeProvider.when('/:language', {templateUrl: 'partials/home.html', controller: 'HomeCtrl'});
$routeProvider.when('/:language/about', {templateUrl: 'partials/about.html', controller: 'AboutCtrl'});
Then, in a service I get the language parameter with $routeParams and call my translation code with angular-translate to serve the page in the corresponding language.
Everything is working so far.
But, moreover, in the menu bar, I have a select combo box to choose the language. So, when user change the language, the url language parameter should change.
How should I do this?

Here's an example. Like I said in your other question, I'd use ui-router for this.
http://plnkr.co/edit/bCNgS07BblMHz55VBRSQ?p=preview
The language dropdown will preserve the currently selected state. So if you go to home -> paragraph, then change language, you will remain on the paragraph route but the language parameter will change.
app.js:
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/en');
$stateProvider
.state('lang', {
url: '/:language',
templateUrl: 'partial-lang.html',
abstract: true,
controller: function($state, $scope) {
$scope.changeLanguage = function(language) {
$state.go($state.current.name, {language: language});
}
}
})
.state('lang.home', {
url: '',
templateUrl: 'partial-home.html'
})
.state('lang.home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('lang.home.paragraph', {
url: '/paragraph',
template: 'I could sure use a drink right now.'
})
.state('lang.about', {
url: '/about',
templateUrl: 'partial-about.html'
});
});
partial-lang.html:
<nav class="navbar navbar-inverse" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" href="#">AngularUI Router</a>
</div>
<ul class="nav navbar-nav">
<li><a ui-sref=".home">Home</a></li>
<li><a ui-sref=".about">About</a></li>
<li class="dropdown">
<a href class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Language: {{$state.params.language | uppercase}} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href ng-click="changeLanguage('en')">English</a></li>
<li><a href ng-click="changeLanguage('fr')">Français</a></li>
<li><a href ng-click="changeLanguage('ru')">Русский</a></li>
</ul>
</li>
</ul>
</nav>
<div class="container">
<div ui-view></div>
</div>
index.html
<body ng-app="routerApp">
<div ui-view></div>
<pre>
<p>state = {{$state | json}}</p>
</pre>
</body>
The rest of the files are self-explanatory

You bind your combo box to a variable, then you can use $watch to know when it changes, and finally you can user $route to update language.
$watch info
$route info
Update:
Something like this:
$scope.$watch('language', function (newValue, oldValue) {
if (newValue !== oldValue) {
$route.updateParams({language: newValue});
}
});
UPDATE VERSION 1.0.7
$scope.$watch('language', function (newValue, oldValue) {
if (newValue !== oldValue) {
var path = $location.path();
path = path.replace('/'+oldValue+'/', '/'+newValue+'/');
console.log(path);
$location.path(path);
$route.reload();
}
});

Related

are these ui-sref states ie11 friendly

When trying to navigate with IE11, the links do not work with a state such as url: '/main-category/:id' but the "parent" state url: '/:main-category/' works fine. These are not true nested or parent-child since they don't actually share any common views/html template, except for navigation.
I found this meta tag suggestion and this IE events suggestion, but neither seem to be providing a solution for why my navigation bar AND links from another state do not function.
Here is a live site without minify, to test compare IE11 & all other browsers.
So, are these routes correctly setup?
router.js
angular.module('app_litsco')
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function ($stateProvider, $urlRouterProvider, $locationProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', { //this state works
url: '/',
templateUrl: 'html/home.html',
controller: 'controller_home',
})
.state('product_streamline', { //this state DOES NOT work
url: '/streamline_metal_panels/:id',
templateUrl: 'html/template_product.html',
controller: 'controller_prods',
})
.state('streamline_panels', { //this state works
url: '/:cat',
templateUrl: 'html/template_productlist.html',
controller: 'controller_productlist',
})
$locationProvider.html5Mode(true);
}]);
index.html
example NavBar section
<li class="link">
<!-- Main Category link -->
<a data-activates="streamline-panel-dropdown" class="blue-text text-darken-4 product-link" ui-sref="streamline_panels({ cat: 'streamline_metal_panels' })">STREAMLINE METAL PANELS</a>
<div id="streamline-panel-dropdown" class="dropdown-content dropdown-full full">
<div class="container dropdown-container flex-row-around-center">
<div class="col sm12 m3 text-center dropdown-item">
<!-- Sub-Category links -->
<a ui-sref="product_streamline({ id: 'classic_cr' })" class="product-link">Classic CR</a>
</div>
<div class="col sm12 m3 text-center dropdown-item">
<a ui-sref="product_streamline({ id: 'omniwall_md' })" class="product-link">Omniwall MD</a>
</div>
<div class="col sm12 m3 text-center dropdown-item">
<a ui-sref="product_streamline({ id: 'omniwall_cl' })" class="product-link">Omniwall CL</a>
</div>
</div>
</div>
</li>
Your product_streamline state uses Array.find() in a resolve, which IE doesn't support.
.state('product_streamline', {
url: '/streamline_metal_panels/:id',
templateUrl: 'html/template_product.html',
controller: 'controller_prods',
resolve: {
product: ['factory_litsco', '$stateParams', function (factory_litsco, $stateParams) {
var productIdObj = factory_litsco.find(function (obj) {
if (obj.id === $stateParams.id) {
return obj;
}
});
return productIdObj;
}],
$title: ['product', function (product) {
return product.productName;
}]
}
})
To detect these sort of errors, add a handler for $stateChangeError such as:
angular.element(document.getElementsByTagName('body')[0]).scope().$on('$stateChangeError', function() { console.log("Error", arguments) } )

ng-click not working with mmenu

I trying to use mmenu menu http://mmenu.frebsite.nl/ with my angularJS base app.
ui-sref is working but ng-click is not working with mmenu link.
Can anyone help me, what wrong I am doing it.
Main HTML Page
<body ng-app="student">
</div>
<div class="header">menu</div>
<div class="content">
<div ui-view></div>
</div>
<nav id="menu">
<ul>
<li ><a ng-href="" ng-click="holidayInfo()">Holiday</a></li>
<li><a ui-sref="notices">Notices</a></li>
</ul>
</nav>
</div>
</body>
app.js
app.config(['$urlRouterProvider', '$stateProvider', '$locationProvider','RestangularProvider',
function ($urlRouterProvider, $stateProvider, $locationProvider, RestangularProvider ) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state("home", {
url: "/",
templateUrl: 'home.html',
controller:'homeCtrl as vm',
resolve: {
currentyear: function (Restangular, $stateParams) {
return Restangular.one('CurrentACYear').get();
}
}
})
.state("notices", {
url: "/notices",
templateUrl: 'notices.html',
controller:'schoolNoticesCtrl as vm',
resolve: {
notciesList: function (Restangular, $stateParams) {
return Restangular.one('notices').get();
}
}
})
My holidayInfo() method is in homeCtrl controller.
When I do <nav id="menu" ng-controller="homeCtrl"> it throw error, not able to resolve currentyear .
You had # in the href of Holiday which is executing $urlRouter.otherwise code which is fallback URL.
It should be kept as blank
ng-href=""
Other thing would be you havent't had nav bar wrap with scope of controller where you wrote holidayInfo method. There you can have new controller for your navbar component & have holidayInfo method over there.
Markup
<nav id="menu" ng-controller="navbarCtrl">
<ul>
<li><a ng-href="#" ng-click="holidayInfo()">Holiday</a></li>
<li><a ui-sref="notices">Notices</a></li>
</ul>
</nav>
Code
app.controller('navbarCtrl', function(){
$scope.holidayInfo = function () {
console.log("i am trigeered ....")
};
})
Do update question with more info if you still found an issue.

AngularJS & Spring MVC - set templateUrl with $stateProvider

I am developing a web application using angular js & spring-mvc.
In the header of my application
header.jsp
<nav class="navbar navbar-default navbar-fixed-top" >
<div class="container">
<div class="navbar-header" >
<a class="navbar-brand"
href="<c:url value="/home"/>" >Pipeline Tracker
</a >
</div >
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li ui-sref-active="active">
<a ui-sref="empty">PIPELINE</a>
</li>
<li ui-sref-active="active">
<a ui-sref="employees">ADMIN</a>
</li>
</ul>
</div>
</div>
</nav>
app.js
app.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('empty', {
url: '/',
view: {
'main':{
templateUrl: 'views/templates/empty.html',
controller: 'EmptyController'
}
}
})
.state('employees',{
url: '/admin/employees',
view: {
'main':{
templateUrl: 'http://localhost:8070/PipelineClient/views/templates/employee.jsp',
controller: 'TestController'
}
}
});
$urlRouterProvider.otherwise('/');
});
I am unable to set the templateUrl property, so my app navigates perfectly. I can see url changing accordingly to '#/' and '#/admin/employees' but nothing is displayed. There is no error in the console also.
I tried to set templateUrl two ways. One with html and another with jsp using context url. None of them working.
I removed routing and checked the data loads pefectly. No issue with data loading.
for loading dependencies:
var app = angular.module('pipeline', [
'ngResource',
'infinite-scroll',
'angularSpinner',
'jcs-autoValidate',
'angular-ladda',
'mgcrea.ngStrap',
'toaster',
'ngAnimate',
'ui.router'
]);
index.jsp
<div class="container main-content">
<div ui-view="main"></div>
</div>
How to set this templateUrl property? I am relatively new to angular, so do let know if you need any more info on this.
I believe it's a syntax error, should be "views" and not "view".
.state('employees',{
url: '/admin/employees',
views: { <--- change view to views
'main':{
templateUrl: 'http://localhost:8070/PipelineClient/views/templates/employee.jsp',
controller: 'TestController'
}
}
});
I'm not familiar with the "view:" syntax you're using. does it work if you do:
.state("test", {
templateUrl: "views/templates/empty.html",
controller: 'EmptyController'
}
http://localhost:8070/#/test

Undefined Controller AngularJS

My template is loading but I get an error that the controller is undefined. The controller does exist in my sources exactly at the location defined. What is wrong with this code?
Error: [ng:areq] Argument '/Scripts/controllers/wizardNavigationCtrl.js' is not a function, got undefined
http://errors.angularjs.org/1.2.14/ng/areq?p0=%2FScripts%2Fcontrollers%2FwizardNavigationCtrl.js&p1=not%20aNaNunction%2C%20got%20undefined
Index.html (root page)
<div class="container">
<div ui-view></div>
</div>
wizardLayout.html
<div>
<ul class="nav nav-tabs">
<li ng-repeat="model in models" ng-class="active: model.active">
<a ui-sref=".model-{{model.name}}">model.name</a>
</li>
<li ng-class="navStep=='addnew' ? 'active' : ''">
<a ui-sref=".newmodel"><span class="glyphicon glyphicon-plus"></span></a>
</li>
<li><a ui-sref=".other">Other</a></li>
<li><a ui-sref=".customer">Customer Info</a></li>
<li><a ui-sref=".shipping">Shipping Info</a></li>
<li><a ui-sref=".review">Review</a></li>
</ul>
</div>
<div ui-view>
</div>
app.js:
'use strict';
var myApp = angular.module('myApp', ['ui.router']);
myApp.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('wizard', {
url: '/',
templateUrl: 'Scripts/templates/wizardLayout.html',
controller: 'Scripts/controllers/wizardNavigationCtrl.js'
})
.state('wizard.newmodel', {
url: '/newmodel',
templateUrl: 'Scripts/templates/wizardModel.html',
controller: 'Scripts/controllers/wizardModelCtrl.js'
})
.state('wizard.other', {
url: '/other',
templateUrl: 'Scripts/templates/wizardOther.html',
controller: 'Scripts/controllers/wizardOtherCtrl.js'
})
.state('wizard.customer', {
url: '/customer',
templateUrl: 'Scripts/templates/wizardCustomer.html',
controller: 'Scripts/controllers/wizardCustomerCtrl.js'
})
.state('wizard.shipping', {
url: '/shipping',
templateUrl: 'Scripts/templates/wizardShipping.html',
controller: 'Scripts/controllers/wizardShippingCtrl.js'
})
.state('wizard.review', {
url: '/review',
templateUrl: 'Scripts/templates/wizardReview.html',
controller: 'Scripts/controllers/wizardReviewCtrl.js'
});
}]);
wizardNavigationCtrl.js
myApp.controller('wizardNavigationCtrl', ['wizardSvc', '$scope', function (wizardSvc, $scope) {
alert('navigation controller! ' + wizardSvc.quote.title);
$scope.navStep = 'addnew';
wizardSvc.init();
}])
The controller should be in the format MyControllerName not the name and path to the javascript file. That in mind, in order to use the controller the javascript must have been loaded by the browser already. This requires the use of a traditional script tag because angular won't find or load these scripts for you.
Taking a snippet from your code it becomes:
.state('wizard', {
url: '/',
templateUrl: 'Scripts/templates/wizardLayout.html',
controller: 'wizardNavigationCtrl'
})
And somewhere in the page you need:
<script src="Scripts/controllers/wizardNavigationCtrl.js" type="text/javascript"></script>
Hope that helps!

Angular UI Router: How do I get parent view to be "active" when navigating to nested view?

I'm working on a project that has implemented the UI router and it's using ui-sref-active="active" to add the active class to the navigation menu item when that item is the current route. However, when you navigate to a nested view within that view, the parent menu item is no longer active. See the following Plunker:
http://plnkr.co/edit/2CoEdS?p=preview
By default (or if you click on it) Route 1 is "active". When you click on "Show List," you will see that Route 1 is no longer active.
Edit:
The only difference between this example and my actual project is that the navigation menu in my actual project has its own controller and so does not use the same scope as the controller for "route1".
EDIT For updated ui-router 0.2.13:
ui-sref-active="active" now sets the 'active' class when the current state is the ui-sref's state or any child
ui-sref-active-eq="active" behaves as the previous iterations of ui-sref-active, and only sets the class for the exact state
Original Answer:
See open ui-router issues:
https://github.com/angular-ui/ui-router/issues/704 and 818
A general workaround people are suggesting is:
ng-class="{active:$state.includes('route1')}"
Of course, $state must be added to $scope. See updated plunk: http://plnkr.co/edit/KHLDJP?p=preview
You are having a wrong understanding of ui-sref-active="active"
<li ui-sref-active="active"><a ui-sref="route1">Route 1</a></li>
This will show special css highlighting only when you are in state route1 (reference https://github.com/angular-ui/ui-router/wiki/Quick-Reference#wiki-ui-sref-active). This is the case when you click on route 1. But when you click on "Show list" you are no longer in route1.
Rather you are in state "route1.list" . You can verify this by writing the following code. This is strictly for understanding how state works.
js
inside controller
$rootScope.currentState = $state.$current.name //using rootScope so that you can access the variable anywhere inside html
inside html
{{currentState}}
If you look closely at documentation of ui-sref-active, it not only looks at stateName but also stateParams, hence when you go to substate it no longer changes css. From the sourcecode it becomes clearer.
function update() {
if ($state.$current.self === state && matchesParams()) {
$element.addClass(activeClass);
} else {
$element.removeClass(activeClass);
}// route1===route1.list will not be true.
to solve the problem, remember scope variables are inherited in nested views.
inside controller of route.
$scope.route1Active = true;
in html
<li ng-class={active:route1Active}><a ui-sref="route1">Route 1</a></li>
Angular UI router now supports this natively. See commit https://github.com/angular-ui/ui-router/commit/bf163ad6ce176ce28792696c8302d7cdf5c05a01
My solution was to set:
<li ng-class="{ 'active': state.current.name.indexOf('route1') != -1 }">
The state was already previously added to the controller's scope:
$scope.state = $state;
You do not need to do any thing in the controllers. Here is my example code:
<ul class="nav nav-pills nav-stacked" role="tablist">
<li ui-sref-active="active"><a ui-sref="Booking.Step1" href="#/Booking/Step1">Step1</a></li>
<li ui-sref-active="active"><a ui-sref="Booking.Step2" href="#/Booking/Step2" >Step2</a></li>
<li ui-sref-active="active"><a ui-sref="Booking.Step3" href="#/Booking/Step3">Step3</a></li>
<li ui-sref-active="active"><a ui-sref="Booking.Step4" href="#/Booking/Step4">Step4</a></li>
<li ui-sref-active="active"><a ui-sref="Booking.Step5" href="#/Booking/Step5">Step5</a></li>
</ul>
In route configuration:
$stateProvider.state('Booking', {
abstract: true,
url: '/Booking',
templateUrl: "TourApp/Templates/Booking/SideMenu.html",
controller: "SideMenuController"
});
$stateProvider.state('Booking.Step1', {
url: "/Step1",
views: {
'content': {
templateUrl: "TourApp/Templates/Booking/Step1.html",
controller: "Step1Controller"
}
}
});
$stateProvider.state('Booking.Step2', {
url: "/Step2",
views: {
'content': {
templateUrl: "TourApp/Templates/Booking/Step2.html",
controller: "Step2Controller"
}
}
});
Now they have updated and the new way to do that is
<a ui-sref-active="{'active': 'main.app.manage.**'}" ui-sref="main.app.manage.people.list"></a>
Below is the state file
angular.module('manage.people', ['people.invite'])
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('main.app.manage.people', {
url: '/people',
abstract: true,
cache: false,
views: {
'tabContent': {
template: '<div ui-view="peopleContent"></div>',
controller: 'peopleController'
}
}
})
.state('main.app.manage.people.list', {
url: '/list',
views: {
'peopleContent': {
templateUrl: '/internal/main/app/manage/people/views/people.html',
controller: 'peopleListController'
}
}
});
We already have a solution without any "hack" HERE
That's the way to do:
HTML >
<li ui-sref-active="active" >
<a href="#" class="submenu" ui-sref="bands">
<i class="fa fa-location-arrow" aria-hidden="true"></i>
Bands
<span class="fa fa-chevron-down"></span>
</a>
<ul class="nav child_menu">
<li ui-sref-active="active">
<a ui-sref="bands.nirvana">
Nirvana
</a>
</li>
<li ui-sref-active="active">
<a ui-sref="bands.iron">
Iron
</a>
</li>
<li ui-sref-active="active">
<a ui-sref="bands.metalica">
Metalica
</a>
</li>
</ul>
Our router config will be like this >
$stateProvider.state('bands', {
abstract: true,
url: '/bands',
templateUrl: "myapp/categories/template.bands.html", //<ui-view></ui-view>
controller: "SomeController as vm"
}).state('bands.nirvana', {
url: '/nirvana',
templateUrl: "myapp/categories/band.nirvana.html",
controller: "SomeController as vm"
}).state('bands.iron', {
url: '/iron',
templateUrl: "myapp/categories/band.iron.html",
controller: "SomeController as vm"
}).state('bands.meatlica', {
url: '/metalica',
templateUrl: "myapp/categories/band.metalica.html",
controller: "SomeController as vm"
})
I have come here 2 years later the question was asked but angular-ui-router has much proficient approach in solving this issue. It worked for nested states too.
<ul>
<li ui-sref-active="active" class="item">
<a ui-sref="home">Home</a>
</li>
<!-- ... -->
</ul>
When app navigates to home state, this is how resulting HTML will appear :
<ul>
<li ui-sref-active="active" class="item active">
<a ui-sref="home">Home</a>
</li>
<!-- ... -->
</ul>
ui-sref-active quick reference :
A directive working alongside ui-sref to add classes to an element
when the related ui-sref directive's state is active, and removing
them when it is inactive. The primary use-case is to simplify the
special appearance of navigation menus relying on ui-sref, by having
the "active" state's menu button appear different, distinguishing it
from the inactive menu items.
Complete documentation.
I hope this is what you have been looking for.Place the parent url in the list class ,now whenever you navigate to child class parent class will be active
Step 1: Add a Controller for your nav bar orin your existing controller where nav bar is included add the following
app.controller('navCtrl', ['$scope', '$location', function($scope, $location) {
$scope.isActive = function(destination) {
return destination === $location.path();
}
}]);
Step2: In your nav bar
<li ng-class="{active: isActive('/home')}"><a ui-sref="app.home">Browse Journal</a></li>
Thats it.

Resources