i18next - strings don't update when the language is changed - angularjs

I am integrating the i18next library with my Angular 1.5.3 project.
https://github.com/i18next/ng-i18next
When I use the changeLanguage feature the strings on my page don't update to the correct language.
Here's my html:
<!DOCTYPE html>
<html lang="en">
<head>
<script data-require="angular.js#1.5.3" data-semver="1.5.3" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular-sanitize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/8.4.1/i18next.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ng-i18next/1.0.4/ng-i18next.min.js"></script>
<script src="app.js"></script>
<meta charset="utf-8" />
</head>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
Hello, {{name}}!
<div ng-i18next="HELLO"></div>
<button ng-click="change()">change lng</button>
</div>
</body>
</html>
Here's my app.js
if (window.i18next) {
window.i18next.init({
debug: true,
lng: 'en', // If not given, i18n will detect the browser language.
fallbackLng: 'en', // Default is dev
resources: {},
useCookie: false,
useLocalStorage: false,
joinArrays: '<br />'
});
}
var myApp = angular.module('myApp', ['ngSanitize', 'jm.i18next']);
myApp.controller('MyCtrl', MyCtrl);
function MyCtrl($scope, $i18next) {
$scope.name = 'Superhero';
$scope.change = function() {
$i18next.i18n.changeLanguage('fr', function(err, t) {
console.log(t('HELLO')); //prints out bonjour
});
console.log($i18next.t('HELLO')); //prints out hiya
};
$i18next.i18n.addResourceBundle('en', 'translation', {
'HELLO': 'hiya'
});
$i18next.i18n.addResourceBundle('fr', 'translation', {
'HELLO': 'bonjour'
});
console.log($i18next.t('HELLO')); //prints out hiya
}
Plunkr: https://plnkr.co/edit/7oI13bNJVqVgTEFOT6bk
I don't know how I can 'refresh' my page to get the correct translations.

Working plunker
Use this to change the language $i18next.changeLanguage('fr');
Replace:
$i18next.i18n.changeLanguage('fr', function(err, t) {
console.log(t('HELLO')); //prints out bonjour
});
with this:
$i18next.changeLanguage('fr');
Translations in the controller
For translations to be performed in the controller after a language change happens, listen for the i18nextLanguageChange event and then perform translations like below.
// Listen for the language change event and perform $scope bindings
$scope.$on('i18nextLanguageChange', function () {
// Inside a timeout to allow the language change to complete
$timeout(function () {
console.log($i18next.t('HELLO'));}
);
});

Related

Papaparse Angular service

Coding is new to me and I tried to play with Papaparse and AngularJS. My code is below. I seem to miss something and hope someone could help me.
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="mainController">
<p>{{test}}</p>
</div>
<script>
var app = angular.module('myApp', []);
app.service('papa', function() {
Papa.parse("test.csv", {
download: true,
header: true,
dynamicTyping: true,
complete: function(results) {
console.log(results.data);
}
})
});
app.controller('mainController', function($scope, papa) {
$scope.test = papa;
console.log($scope.test);
});
</script>
</body>
</html>

Google Signin button in AngularJS sometimes does not show up

I followed this link https://developers.google.com/identity/sign-in/web/sign-in to get Google Signin on Angular-based website.
I have seen some weird behavior. The signin button sometimes show but not always. When I refresh a page, only 1 in 5 refreshes, the button appears.
I tried in Chrome and Safari and both has same behavior.
Code:
index.html
<script src="https://apis.google.com/js/platform.js" async defer></script>
<meta name="google-signin-client_id" content="my_client_id">
login.html
<div class="g-signin2" data-onsuccess="onSignIn"></div>
login.js
angular.module('app').controller('LoginCtrl', function($scope) {
window.onSignIn = function(googleUser) {
// Get some info
}
});
My guess is that the platform.js script waits for the DOM-ready event and then looks for your div with the "g-signin2"-class. Though, in Angularjs things work a little different. The reason that it works sometimes, is because sometimes your div has been rendered by Angular and sometimes is hasn't been rendered before the Dom-ready event.
There's documentation on how to get the same result with your own javascript.
I made an example that follows your routing.
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div ng-view></div>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.6/angular-route.min.js"></script>
<script>
angular.module('app',['ngRoute'])
.config(['$routeProvider',function($routeProvider){
$routeProvider
.when('/log-in', {
template: '<button ng-click="logInCtrl.onLogInButtonClick()">Log In</button>',
controller: 'LogInController',
controllerAs: 'logInCtrl'
}).otherwise({
redirectTo:'/log-in'
});
}])
.controller('LogInController',function(){
var self = this; //to be able to reference to it in a callback, you could use $scope instead
gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
gapi.auth2.init(
{
client_id: 'CLIENT_ID.apps.googleusercontent.com'
}
);
var GoogleAuth = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
self.onLogInButtonClick=function(){//add a function to the controller so ng-click can bind to it
GoogleAuth.signIn().then(function(response){//request to sign in
console.log(response);
});
};
});
});
</script>
</body>
</html>
Or as a directive:
<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<google-sign-in-button on-sign-in="onSignIn(response)" g-client-id="CLIENTID.apps.googleusercontent.com"></google-sign-in-button>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script>
angular.module('app',[])
.controller('MainController',['$scope', function ($scope) {
$scope.onSignIn=function(response){
console.log(response);
}
}])
.directive('googleSignInButton',function(){
return {
scope:{
gClientId:'#',
callback: '&onSignIn'
},
template: '<button ng-click="onSignInButtonClick()">Sign in</button>',
controller: ['$scope','$attrs',function($scope, $attrs){
gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
gapi.auth2.init(
{
client_id: $attrs.gClientId
}
);
var GoogleAuth = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
$scope.onSignInButtonClick=function(){//add a function to the controller so ng-click can bind to it
GoogleAuth.signIn().then(function(response){//request to sign in
$scope.callback({response:response});
});
};
});
}]
};
});
</script>
</body>
</html>
After writing the previous examples I found a better and easier way to implement it. With this code you inherit the same button as you normally would.
<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="google-signin-client_id" content="CLIENTID.apps.googleusercontent.com">
</head>
<body>
<google-sign-in-button button-id="uniqueid" options="options"></google-sign-in-button>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script>
angular.module('app', [])
.controller('MainController', ['$scope',
function($scope) {
//for more options visit https://developers.google.com/identity/sign-in/web/reference#gapisignin2renderwzxhzdk114idwzxhzdk115_wzxhzdk116optionswzxhzdk117
$scope.options = {
'onsuccess': function(response) {
console.log(response);
}
}
}
])
.directive('googleSignInButton', function() {
return {
scope: {
buttonId: '#',
options: '&'
},
template: '<div></div>',
link: function(scope, element, attrs) {
var div = element.find('div')[0];
div.id = attrs.buttonId;
gapi.signin2.render(div.id, scope.options()); //render a google button, first argument is an id, second options
}
};
});
</script>
</body>
</html>
I found another way to solve the problem a liitle bit simply.
The explanation from #sniel is perfect but I will let you know more simple solution.
you can use below sample code very simiraliry with using $watch
https://developers.google.com/identity/sign-in/web/build-button
<!-- html part -->
<div id="signInButton"></div>
//gapi is Asynchronously loaded so you need to watch
$scope.$watch('gapi', function(newValue,oldVale){
if(newValue !== oldVale){
if(gapi){
gapi.signin2.render('signInButton',
{
'onsuccess': $scope.onSuccess,
'onfailure': $scope.onFailure,
'scope':'https://www.googleapis.com/auth/plus.login'
}
);
}
}
});

$compile() does not fire in custom Angular directives

I have the following directive:
angular.module("example_module", [])
.directive("example_directive", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
and the following HTML code:
<!DOCTYPE html>
<html ng-app="example_module">
<head>
<meta charset="utf-8">
<title>Example title</title>
<script src="lib/angular/angular.min.js"></script>
<script src="js/example.js"></script>
</head>
<body>
<div example_directive></div>
</body>
</html>
I would expect the directive to compile to Hello world, but it compile to an empty string instead. Where is the error?
I could use template or a link function, but my goal here is to understand how the compile function works.
This has to do with how angular handles directive names. I've changed your example to match angular's naming conventions and put together a Plunk
angular.module("example_module", [])
.directive("exampleDirective", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
<body>
<div example-directive></div>
</body>

Which is the better way to provide API in AngularJS library

I am building a third-party library using AngularJS and I want to provide some API for user to interact with some internal properties/functions. Below are two solutions I'm considering, I want to know if some best practice for such a requirement.
Wrap the API in an scope object which can be accessed by the App.
angular.module('lib', []).directive('libDirective', function() {
return {
scope: {
api: '='
},
link: function(scope, elem, attrs) {
scope.api = {
sayHi: function() {
return 'Hi from lib';
}
};
}
};
});
angular.module('app', ['lib']).controller('AppCtrl', function($scope) {
// to do
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
</head>
<body ng-app="app">
<div ng-controller="AppCtrl">
<lib-directive api="lib"></lib-directive>
{{lib.sayHi()}}
</div>
</body>
</html>
Wrap the API in a service which can be injected into the App module.
angular.module('lib', []).factory('libService', function() {
var hi = 'Hi from lib';
// return API
return {
sayHi: function() {
return hi;
}
};
});
angular.module('app', ['lib']).controller('AppCtrl', function($scope, libService) {
$scope.libHi = libService.sayHi();
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
</head>
<body ng-app="app">
<div ng-controller="AppCtrl">
{{libHi}}
</div>
</body>
</html>

Watch not working when HTML loaded from Directive in AngularJS

I am loading a partial in Angular dependant on the route of the URL.
When I load the partial it loads, and responds to the Controllers functions. However I have a directive which has a watcher. This does not work when I use the
It works fine when I load the HTML inside the main page. I have a Plunker of this here
http://plnkr.co/edit/DK33pIrp0HyhUOjwm5X2?p=preview
Essentially clicking "hello" should change the $scope.origin and the watcher should then fire its event. It does not.
My HTML:
<!DOCTYPE html>
<html ng-app="App">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.1.2/coffee-script.min.js"></script>
<script src="app.js"></script>
<script src="directive.js"></script>
</head>
<body ng-controller="MapCtrl">
<ng-view></ng-view>
</body>
</html>
app.js
var app;
app = angular.module("App", []);
app.config(function($routeProvider) {
return $routeProvider.when("/", {
templateUrl: "home.html",
controller: MapCtrl
});
});
this.MapCtrl = function($scope) {
return $scope.clicked = function() {
console.log("clicked");
$scope.origin = Math.floor(Math.random() * 11);
return console.log($scope.origin);
};
};
directive.js
(function(angular) {
var app;
app = angular.module("App");
return app.directive("leaflet", function() {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<section id='map' class='map'></section>",
scope: {
origin: "=origin"
},
controller: function($scope, $attrs) {
return $scope.$watch("origin", (function(newValue, oldValue) {
return alert("its changed");
}), true);
}
};
});
})(angular);
home.html
<button ng-click="clicked()">hello</button>
how can I get this working?
edit: I have just made this pure JS and not coffeescript.
Thanks so all who helped me find the issue.
This can be done by setting the
<button ng-click="clicked()">hello</button>
to
<button ng-click="$parent.clicked()">hello</button>
This is because the ng-view will be a child. This simple fix is now working.

Resources