AngularJS : Setting languages in angular-translate from Controller or Service - angularjs

I am interested in using angular-translate.
Due to a lot of setup calls that happen initially on startup, I cannot provide the language json during config. Nor is it possible to use the async loader.
I need to be able to specify the languages from a controller or service at a later point.
$translateProvider.translations(.., ...) is the call to use, but $translateProvider isn't available in controllers or services, but seemingly only at config.
$translate doesn't seem to have the ability to load a language JSON.
Is there any workaround?

First inject $translate into your controller.
app.controller('MainCtrl', function($scope, $state, $translate) {});
Then you can get and set current language with $translate.use().
var lang = $translate.use(); // holds current lang
$translate.use(lang); // sets lang to use
If you need to add new translations after config, then you can use partial loaders.
// config example
app.config(function($translateProvider, $translatePartialLoaderProvider){
// "known" translations here, in {lang}.main.json, if any
$translatePartialLoaderProvider.addPart('main');
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: '/path/to/files/{lang}.{part}.json'
});
});
// controller
app.controller('MainCtrl', function($scope, $translate, $translatePartialLoader){
$translatePartialLoader.addPart('translation');
$translate.refresh();
$translate.use('en');
});
// en.translation.json
{ "KEY" : "Value", ... }
If that is not dynamic enough, then you can always do the translation on-the-fly.
// config
app.config(function($translateProvider, $translatePartialLoaderProvider){
$translateProvider.preferredLanguage('en');
$translateProvider.translations('en',{
'TITLE': '{{ title }}',
'SUBTITLE': '{{ subtitle }}',
'STATIC': 'This is static'
});
});
// controller
app.controller('MainCtrl', function($scope, $translate){
$scope.dynamic = {
'title': 'This is my header',
'subtitle': 'My subtitle'
};
});
// template
<pre>{{ 'TITLE' | translate:dynamic }}</pre>
<pre>{{ 'SUBTITLE' | translate:dynamic }}</pre>
<pre>{{ 'STATIC' | translate }}</pre>
This would spit out something like

Got there in the end.
in the .config
$translateProvider.useLoader('customLoader');
the customLoader...
angular.module('myapp').factory('customLoader', function ($q, TranslationService) {
return function (options) {
var deferred = $q.defer();
deferred.resolve(TranslationService.getLanguages().filter(function(lang){
return lang.key == options.key
})[0].values);
return deferred.promise;
};
});
and then a TranslationService to share the data
angular.module('myapp').factory('TranslationService', function () {
var languages = [];
return {
setLanguages: function (data) {
languages = data;
},
getLanguages: function () {
return languages;
}
}
});

Maybe check this:
http://www.ng-newsletter.com/posts/angular-translate.html
Under "Switching the language at runtime"
$translate.use(); // Returns the currently used language key
$translate.use('en'); // Sets the active language to `en`
app.controller('TranslateController', function($translate, $scope) {
$scope.changeLanguage = function (langKey) {
$translate.use(langKey);
};
});

this one works. storageService has local storage and after setting 'NG_TRANSLATE_LANG_KEY' in local storage. You can call it like below.
angular.module('myApp').run(['$rootScope', 'StorageService', function($rootScope, StorageService) {
$rootScope.currentLanguage = StorageService.local.get('NG_TRANSLATE_LANG_KEY') || 'en';
}]);
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1" ng-controller="TranslateController" ng-init="changeLanguage(currentLanguage)">
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" ng-click="changeLanguage('tr')">TR</a></li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" ng-click="changeLanguage('en')">EN</a></li>
</ul>

I think the best way to manage dynamically loading is
in the resolve config router block like
resolve: {
translatePartialLoader: function loadPartialLoader($translate,$translatePartialLoader) {
$translatePartialLoader.addPart('home');
return $translate.refresh();
}
}

Related

Error: [$controller:ctrlreg] The controller with the name 'CounterController' is not registered.

I see this error when I press F12 in chrome.
There seems to be no problems.
My html code
<ul class="dropdown-menu" role="menu" ng-controller="CounterController">
<li ng-repeat="item in messages">{{item.name}}</li>
</ul>
Here is my js file. Any idea why?
The version of angualr I am using is 1.6.1
angular controller code
angular
.module('myApp.counter_controller', [])
.controller('CounterController', ['$scope', '$filter', function($scope, $filter) {
'use strict';
$scope.messages = [{
name : 'ENG',
read : false
}, {
name : 'JPN',
read : false
}, {
name : 'CHI',
read : false
}, ];
$scope.setRead = function(item, $event) {
$event.preventDefault();
$event.stopPropagation();
item.read = true;
};
$scope.setUnread = function(item, $event) {
$event.preventDefault();
$event.stopPropagation();
item.read = false;
};
$scope.setReadAll = function($event) {
$event.preventDefault();
$event.stopPropagation();
angular.forEach($scope.messages, function(item) {
item.read = true;
});
};
$scope.unseenCount = $filter('filter')($scope.messages, {
read: false
}).length;
$scope.$watch('messages', function(messages) {
$scope.unseenCount = $filter('filter')(messages, {
read: false
}).length;
}, true);
}]);
You may have done some file arranging problem.
please see code it is working fine :code link
output:Main Output
you may need to wrap them in an anonymous function like so
(function(){
var myApp = angular.module('myAPP', ['ngRoute']);
storehubs.controller('welcome',['$scope',function($scope){
$scope.pageClass="welcome_page";
}]);
})();
Also your html should include ng-app="myApp" and your links to angular.min.js and yourapp.js files need to be in the head tag of your html.
Look at some docs https://docs.angularjs.org/guide/controller

Testing angular components templateUrl

I'm having an issue testing this template html. I'm using karma and sinon. I'm getting this Unknown provider: _Provider <- _ <- service error.
Here is my component:
angular.
.module('app.component.thing-foo')
.component('thingFoo', {
templateUrl: 'components/thing-foo/thing-foo.html',
controller: ThingFooController,
bindings: {
thing: '<',
onSelect: '&'
}
});
function ThingFooController($log, service) {
var ctrl = this;
ctrl.$onInit = $onInit;
ctrl.$onChanges = $onChanges;
ctrl.thingList = [];
function $onInit(){
getThingList();
}
function $onChanges(changesObj){
if (changesObj.thing) {
ctrl.thing = angular.copy(changesObj.thing.currentValue);
}
function getThingList(){
service.getThings()
.then(function (result) {
ctrl.thingList = result.things;
}, function (error) {
$log.error('Did not work: $0', error);
})
}
function selectionChanged(selected) {
ctrl.onSelect({thing: selected});
}
}
thing-foo.html:
<span ui-dropdown class="dropdown-toggle">
<a href id="query-menu-label" ui-dropdown-toggle>
Thing Picker <b class="caret"></b>
</a>
<ul calss="dropdown-menu" aria-labelledby="query-menu-label" role="menu">
<li role="presentation" ng-repeat="thing in $ctrl.thingList">
<a role="menuitem" ng-click="$ctrl.selectionChanged(thing)">{{ thing }}</a>
</li>
</ul>
</span>
I'm including the parts of the spec file that are failing:
beforeEach(inject(function(_$compile_, _$componentController_, _$rootScope_){
$compile = _$compile_;
$componentController = _$componentController_;
$rootScope = _$rootScope_;
}));
beforeEach(function(){
scope = $rootScope.$new();
element = angular.element('<thing-foo></thing-foo>');
service = {
getThings: sinon.stub();
};
locals = {
service: service
};
});
it('loads html', function(){
var tag = $compile(element)(scope);
scope.$apply();
expect(tag.html()).to.exist();
});
There's a lot of code here but I wanted to make sure I included everything. I think the problem is the $ctrl.getThingList in the ng-repeat. Am I supposed to inject the service some how? If so how do I do that? I don't have a lot of practice with writing unit tests for angular. Any help is appreciated.
Not entirely sure, but maybe try to configure $provider to inject your service?
Something like this:
// Put this before your first beforeEach
beforeEach(module(function($provide) {
$provide.value('service', service);
}

Angularjs ng-click inside ng-repeat

I'm trying to load a list of clickable news feed URLs in a dropdown-menu. When I use fixed addresses in view, it works fine but when I populate the addresses using controller, dropdown menu is fine but ng-click doesn't work as expected.
Here is jsfiddle of working version:http://jsfiddle.net/mahbub/b8Wcz/
This code works:
<ul class="dropdown-menu">
<li>ABC News
</li>
<li>CNN
</li>
</ul>
controller code:
var App = angular.module('RSSFeedApp', []);
App.controller("FeedCtrl", ['$scope', 'FeedService', function ($scope, Feed) {
$scope.loadButonText = "Select news channel";
$scope.loadFeed = function (e) {
Feed.parseFeed($scope.feedSrc).then(function (res) {
$scope.loadButonText = angular.element(e.target).text();
$scope.feeds = res.data.responseData.feed.entries;
});
}
App.factory('FeedService', ['$http', function ($http) {
return {
parseFeed: function (url) {
return $http.jsonp('//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=' + encodeURIComponent(url));
}
}
}]);
This code does not:
<ul class="dropdown-menu">
<li ng-repeat =" newsChannel in channels">
{{newsChannel.title}}
</li>
</ul>
controller code:
App.controller("FeedCtrl", ['$scope', 'FeedService', function ($scope, Feed) {
$scope.loadButonText = "Select news channel";
$scope.loadFeed = function (e) {
Feed.parseFeed($scope.feedSrc).then(function (res) {
$scope.loadButonText = angular.element(e.target).text();
$scope.feeds = res.data.responseData.feed.entries;
});
}
$scope.channels = [
{
'src': 'http://www.abc.net.au/news/feed/45910/rss.xml',
'title': 'ABC News'
},
{
'src': 'http://rss.cnn.com/rss/cnn_topstories.rss',
'title': 'CNN'
}
];
}]);
App.factory('FeedService', ['$http', function ($http) {
return {
parseFeed: function (url) {
return $http.jsonp('//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=' + encodeURIComponent(url));
}
}
}]);
TypeError: Cannot read property 'feed' of null
Any idea why the $scope.feedSrc doesn't get the feedSrc url?
Thanks
So I recommend using $parent for this. ng-repeat has it's own scope so when you say "feedSrc" it is looking on the ng-repeat scope for that data. "newsChannel" is available because that gets added by ng repeat but you will notice that is never added to your parent controller's $scope.
<ul class="dropdown-menu">
<li ng-repeat =" newsChannel in channels">
{{newsChannel.title}}
</li>
</ul>
As an aside, you might consider not doing assignments in the view like that, it is generally preferable to only assign variables in the controller and make sure that your HTML is read only
The problem is how you are passing arguments to your ng-click:
ng-click="$parent.feedSrc='{{newsChannel.src}}';..."
You don't need to wrap the prperty in handlebars...
ng-click="$parent.feedSrc='newsChannel.src';..."
should fix your problem.
I found a workaround. Added another parameter to loadFeed method.
<ul class="dropdown-menu">
<li ng-repeat =" newsChannel in channels">
{{newsChannel.title}}
</li>
</ul>
and here is the controller's new version:
App.controller("FeedCtrl", ['$scope', 'FeedService', function ($scope, Feed) {
$scope.loadButonText = "Select news channel";
$scope.loadFeed = function (e, feedUrl) {
Feed.parseFeed(feedUrl).then(function (res) {
$scope.loadButonText = angular.element(e.target).text();
$scope.feeds = res.data.responseData.feed.entries;
});
}
$scope.channels = [
{
'src': 'http://www.abc.net.au/news/feed/45910/rss.xml',
'title': 'ABC News'
},
{
'src': 'http://rss.cnn.com/rss/cnn_topstories.rss',
'title': 'CNN'
}
];
}]);
thanks to this post: ng-click inside ng-repeat

How should you access controller functions from other modules

I am having a hard time understanding how Modules should interact with each other in Angularjs. I would like to break the application into nice small modules, but I cannot seem to find the correct way to have these modules interact with each other.
JSFiddle:
http://jsfiddle.net/jwest80/o5o3sr8q/4/
The code shows a breadcrumb I would like to have at the top of the page. The BreadCrumb is in its own module 'bread' and included inside a parent module 'ngFSCH'.
There is a list outside BreadCrumb controller section whose actions should add breadcrumbs. However, I do not understand the correct way to access this addCrumb function. I can only make it work if it is called from inside the breadcrumb controller section in the markup.
Markup:
<div ng-app="ngFSCH">
<section ng-controller="BreadCrumbsCtrl">
<span ng-repeat="crumb in crumbs" class="breadcrumbs">
<span ng-hide="isLast($index)" ng-click="selectCrumb($index)">{{crumb.text}} > </span>
<span ng-show="isLast($index)">{{crumb.text}}</span>
</span>
</section>
<section>
<h4>Add Some Crumbs</h4>
<ul>
<li>Company</li>
<li>Department</li>
<li>User</li>
</ul>
</section>
</div>
Script:
var ngFSCH = angular.module('ngFSCH', ['bread']);
(function () {
var app = angular.module('bread', []);
app.controller('BreadCrumbsCtrl', ['$scope', '$log', function ($scope, $log) {
$scope.crumbs = [{ text: "Crumb 1", url: "url1" }, { text: "Crumb 2", url: "url2" }];
$scope.isLast = function(index) {
return index === $scope.crumbs.length-1;
}
$scope.addCrumb = function (newCrumb) {
$scope.crumbs.push({ text: newCrumb, url: "TestURL" });
}
$scope.selectCrumb = function (index) {
$log.info($scope.crumbs[index].url);
$scope.crumbs = $scope.crumbs.slice(0, index + 1);
}
}]);
})();
I would encapsulate the bread crumb functionality in a service and create a controller for the section with the links (that add the breadcrumbs). The new controller can then use the service to add and remove crumbs from the array. You can also add the crumbs array into a value.. Your controllers can then expose the add and select features to the tiny portions of html they control without polluting other sections of your page.
Here is the result. Hope it helps!
JSFiddle
Here is the code:
var app = angular.module('bread', []);
app.value('crumbs', [
{ text: "Crumb 1", url: "url1" },
{ text: "Crumb 2", url: "url2" }
]);
app.factory("BreadCrumbsService", ['$log', 'crumbs', function ($log, crumbs) {
var service = {
getCrumbs: getCrumbs,
addCrumb: addCrumb,
selectCrumb: selectCrumb
};
return service;
//I did not add a set crumbs because you can set it directly.
function getCrumbs(){
return crumbs;
}
function addCrumb(newCrumb) {
crumbs.push({
text: newCrumb,
url: "TestURL"
});
}
function selectCrumb(index) {
$log.info(crumbs[index].url);
crumbs = crumbs.slice(0, index + 1);
}
}]);
app.controller('BreadCrumbsCtrl', ['$scope', 'BreadCrumbsService', function ($scope, BreadCrumbsService){
$scope.crumbs = BreadCrumbsService.getCrumbs;
$scope.selectCrumb = BreadCrumbsService.selectCrumb;
$scope.isLast = function (index) {
return index === BreadCrumbsService.getCrumbs().length - 1;
}
}]);
app.controller('AddLinksCtrl', ['$scope', 'BreadCrumbsService', function ($scope, BreadCrumbsService) {
$scope.addCrumb = BreadCrumbsService.addCrumb;
}]);
Here is the links section with the new controller:
<section ng-controller="AddLinksCtrl">
<h4>Add Some Crumbs</h4>
<ul>
<li>Company</li>
<li>Department</li>
<li>User</li>
</ul>
</section>
That is intended because you are working within the scope of the controller. How about moving the ng-controller directive to the containing div where ng-app is?
<div ng-app="ngFSCH" ng-controller="BreadCrumbsCtrl">

AngularJS - scope not refreshing when change route

At first sorry for subject name, I didn't knew how to call it. I was searching for an answer but found nothing helpful or didn't knew how to implement this in my little app. I'm learning AngularJS and i thought a "drink counter" will be good for beginning. So all my app is supose to do is when you click a drink icon it adds an item(type of drink and datetime when) and the problem is that it isn't refreshing the drink list when I change route to /#archive/ it only does when I refresh page, my code is:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'myController',
templateUrl: 'Views/home.html'
})
.when('/archive',
{
controller: 'myController',
templateUrl: 'Views/archive.html'
})
.otherwise({ redirectTo: '/' });
});
myApp.factory('myFactory', function () {
var saved = localStorage.getItem('myData');
var drinks = (localStorage.getItem('myData') !== null) ? JSON.parse(saved) : [];
localStorage.setItem('myData', JSON.stringify(drinks));
var factory = {};
factory.getDrinks = function () {
return drinks;
};
return factory;
});
var controllers = {};
controllers.myController = function ($scope, myFactory, $filter) {
$scope.drinks = [];
init();
function init() {
$scope.drinks = myFactory.getDrinks();
$scope.beerDrinked = $filter('filter')($scope.drinks, 'beer').length;
}
$scope.nowDrinking = function (what) {
$scope.drinks.push({
drinkDate: new Date(),
what: what
});
localStorage.setItem('myData', JSON.stringify($scope.drinks));
};
}
myApp.controller(controllers);
Yes, sure. This is my HTML in archive:
<ul class="drinked" data-ng-repeat="drink in drinks | orderBy: 'drinkDate':true track by $index">
<li class="{{ drink.what }}"><span class="date">{{ drink.drinkDate | date: 'EEEE, d MMMM yyyy' }}</span><span class="time">{{ drink.drinkDate | date: 'HH:mm:ss'}}</span></li>
</ul>
And this is in home:
<span data-ng-bind="beerDrinked"></span>
beer
if you want to refresh your html, you´ve to use ng-repeat, example:
<div class="drinks" data-ng-repeat="drink in drinks"></div>
I don´t know where the user have to click to refresh you page, but to do this you´ve to use data-ng-click directive, example:
<button data-ng-click="addDrink(someDrink)"></button>
So in your controller should be like this, example:
app.controller('MyController', function ($scope) {
$scope.drinks = [];
$scope.addDrink= function (drink) { //note that you don´t need(and don´t should) to pass your drink by parameter...
$scope.drinks.push(drink);
}
});
here, put your ng-repeat inside your li element, like this:
<ul class="drinked">
<li data-ng-repeat="drink in drinks | orderBy: 'drinkDate':true track by $index" class="{{ drink.what }}"><span class="date">{{ drink.drinkDate | date: 'EEEE, d MMMM yyyy' }}</span><span class="time">{{ drink.drinkDate | date: 'HH:mm:ss'}}</span></li>
</ul>
Try it!

Resources