AngularJS: loading async translations issue - javascript

I am building a Website with AngularJS. I ran into problems with translations, got a hacky more or less working solution to it but i am thinking that it can be done a lot better.
I have a main controller which loads translations from database.
$http.get($rootScope.ApiUrl + '/?a=sprache&lang=' + $rootScope.lang).success(function (data) {
$scope.spr = data;
$rootScope.translations = data;
$rootScope.updateTranslations();
});
data is an array formatted like this:
{key: "translation",…}
Further on I have a controller for each state. I hoped it could do something like this:
app.controller('InventoryCtrl', [
'$scope',
'$http',
'$location',
'$state',
'$stateParams',
'$rootScope',
'$uibModalStack',
function ($scope, $http, $location, $state, $stateParams, $rootScope, $uibModalStack) {
$scope.title = $rootscope.translations.myTranslatedTitleForThisState
]);
Obviously this does not work as the get-request is not finished before this Code gets called and therefore $rootscope.translations variable is not set.
Instead I wrote the following. The updateTranslations() function is called from the loadTransition() function in MainController (above) after successfully finishing the get-request.
app.controller('InventoryCtrl', [
'$scope',
'$http',
'$location',
'$state',
'$stateParams',
'$rootScope',
'$uibModalStack',
function ($scope, $http, $location, $state, $stateParams, $rootScope, $uibModalStack) {
$rootScope.updateTranslations = function() {
$rootScope.setMetaTags($rootScope.translations.inventory_title, $rootScope.translations.inventory_description);
$rootScope.updateTranslations();
}
}
]);
I am pretty sure this can be done a lot better. Any ideas?

Maybe this answer is not a direct answer to your problem, but a possible new approach...
We have been using ui-router quite alot in recent Angular projects.
ui-router is ideal for defining routes within the application. More about that subject here.
A nice feature is the state resolvement. Meaning that the state will only resolve when the resolvement promise is resolved.
By example
angular.module('testApp').config(function($stateProvider) {
$stateProvider.state('', {
url: '/',
templateUrl: 'main.html',
resolve: {
labels: function(labelService) {
return labelService.loadLabels();
}
}
});
});
In the above example, the state / will resolve after the $http.get promise behind the loadService.loadLabels() is resolved. Meaning the template will be loaded, after all the resolvements are resolved.
In this case, your view will be loaded - after that all your labels are loaded and accessible by the controller (later the view).
A nice thing is that nested state definitions are perfectly possible.
Meaning, that you could have 1 root state, and many child state of the root state.

Related

How to watch $rootScope on entire angular JS application

I am using angular 1.5.6 and my application is composed of several modules, each module has his own view and controller. My question is how can I watch $rootScope on each of those controllers. I need to watch $rootScope on all controllers because I have a global variable on $rootScope.account and when user refresh the page $rootScope.account is cleared and all my application crash. I would like to watch $rootScope.account on all my modules, so when a user refresh I can handle the refresh and redirect to login page. I thought maybe of a service that I would inject on all each of my modules?
You can do it in two simple ways.
First: include $rootScope into your controller:
app.controller('MyCtrl', [
'$rootScope',
'$scope',
function($rootScope, $scope) {
$rootScope.$watch('myvar', function(newValue, oldValue) {
doSomething();
}
//...
}
]);
Second. If you don't have variables with same names in the other scopes then you can avoid using $rootScope and use just $scope. This will work because every $scope is inherited from $rootScope.
app.controller('MyCtrl', [
'$scope',
function($scope) {
$scope.$watch('myvar', function(newValue, oldValue) {
doSomething();
}
//...
}
]);

Using Cookie value from one ng-app in another ng-app in AngularJS

I know that it isn't good practice to have multiple ng-apps in a project but my project is divided into segments that aren't generally related to each other, plus I am left with more or less no other option.
My problem is the persistence of ng-cookie amidst two separate Javascript files. Let's say I have dashboard1.html that is linked to controller1.js and dashboard2.html linked to controller2.js.
Now, I want to access the cookie value of controller 1 in controller 2. When I try to do this the result says undefined and that means I cannot access the cookie value set by another Javascript file.
Please advise me how to solve this issue.
NOTE : Javascript files have respective ng-app defined and controller for each application.
This is Controller and ng-app part of one module.
var app = angular.module('mainpageapp', ['ngRoute','ngCookies']);
app.controller('ctrlmainpage', ['$scope', '$timeout','$http', '$route', '$routeParams', '$location', '$rootScope', '$cookies', function ($scope, $timeout, $http, $route, $routeParams, $location, $rootScope, $cookies) {
console.log("Csadad");
$cookies.myFavorite = 'TestCookie';
var favoriteCookie = $cookies.myFavorite;
console.log("Cookie" + favoriteCookie);
Now in this ng-app's controller I am setting a cookie, it works properly and I do get expected response on console. This is in file controller1.js
Now I am trying to access the same cookie in controller2.js that is all together bound by different ng-app.
var app = angular.module('myApp', ['ngRoute', 'smart-table', 'ngCookies']);
var id = 'hasdh';
app.controller('dashmerctrl', ['$scope', '$timeout','$http', '$route', '$routeParams', '$location', '$rootScope', '$cookies', function ($scope, $timeout, $http, $route, $routeParams, $location, $rootScope, $cookies)
{
var favoriteCookie1 = $cookies.myFavorite;
console.log("Cookie1" + favoriteCookie1);
Her I get undefined as output.
I want to access that same cookie in this file.
if you wish to share data across tabs or domains you can give zendesk's "cross-storage" a try, just implemented it with AngularJS and it works like a charm.
it utilizes window.postMessage() technique to achieve this behavior.
link: zendesk/cross-storage

Why do angularjs controller declaration have this syntax structure?

I see the following angularjs controller syntax structure all the time.
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ($scope, $interval, $location)
{
}]);
Why the repetition in the parameter names? Why not just like this
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ()
{
}]);
or
angular.module('7minWorkout').controller('WorkoutController',
[
function ($scope, $interval, $location)
{
}]);
The array syntax will help you with minification/uglify of your js code.
angular.module('7minWorkout').controller('WorkoutController',
function ($scope, $interval, $location) {
// code here
});
Will be minified and mangled as:
angular.module('7minWorkout').controller('WorkoutController',
function (a, b, c) {
// code here
});
So Angular won't be able to figure out which dependencies to inject
On the other hand, using the array declaration:
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function ($scope, $interval, $location) {
// code here
}]);
Will be minified as :
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function (a, b, c) {
// code here
}]);
So Angular will know what a, b and c represent.
There's also another way to inject variables if you use your first example code like below:
WorkoutController.$inject = ['$scope', '$interval', '$location'];
or
angular.module('7minWorkout').controller('WorkoutController', /* #ngInject */
function ($scope, $interval, $location) {
// code here
});
which will create the $inject method mentioned above when the code is annotated.
So, there are mainly four kinds of annotation:
Implicit Annotation - the first example code
Inline Array Annotation - the second example code
$inject Property Annotation - the $inject method
$ngInject Comment Annotation - the #ngInject method
ng-annotate
Tools like ng-annotate let you use implicit dependency annotations in your app and automatically add inline array annotations prior to minifying. If you decide to take this approach, you probably want to use ng-strict-di.
For more information, see AngularJS Developer Guide - Using Strict Dependency Injection.
This "repetion" is to make it safe for minification:
AngularJS - Controllers, Dependencies, and Minification
or you can use following syntax, according to popular angular-styleguide https://github.com/johnpapa/angular-styleguide
angular.module('7minWorkout')
.controller('WorkoutController', WorkoutController);
WorkoutController.$inject = ['$scope', '$interval', '$location'];
function WorkoutController($scope, $interval, $location) {
}
You could write the first version since it just omits the parameters of the function which are also accesible via arguments inside the function. So you would avoid the repition but slicing the arguments property is also not really efficient (compared to just type out the parameters).
As the others answers stated the repition is to make it safe for minification.
The first controller syntax makes it possible to minify/uglify the javascript code with the use of tools like ngmin. I'm not quite sure if the 2nd and 3rd options you have provided are viable options to create a controller, but in any case they will not be minified correctly since the tools will not now what the providers are. I would either suggest to use option 1 or option 3 (without the brackets) to create a controller. Note that option 3 will not be minified correctly by automated tools.
Some Useful information about creating controllers:
AngularJS Developer Guide - Controllers
option3 without brackets
angular.module('7minWorkout').controller('WorkoutController', function ($scope, $interval, $location)
{
//Your Code
});

Why does an angularjs dependency array include the function

To inject dependencies, we do the following:
inject(["$scope", "$compile", function ($scope, $compile) {
...
}]);
This syntax is weird! Putting the function inside the array seems really counter-intuitive to me. Is there a good reason why it was chosen like this? Why not
inject(["$scope", "$compile"], function ($scope, $compile) {
My preferred syntax would be:
inject("$scope", "$compile", function ($scope, $compile) {
but I understand that there would be a few performance issues there. (Removed because it seems to be confusing the question.)
Sometimes, you'd want to assign a controller function to a variable, like with ngRoute (which was part of core Angular early on), instead of registering it with .controller:
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: function($scope){
}
});
It helps to re-use the same variable for annotation as well.
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: ["$scope", function($scope){
}]
});
Yes, it's a bit counter-intuitive, but not completely out of the ballpark of reason.
Not only performance:) It's because of the minify things. U can read about:
Angular Inject minify

$filter is not a function AngularJS

app.controller('myController', ['$scope', '$http', '$filter', function($scope, $http, $filter) {
The above is an example of my code where I am trying to use $http.get and also $filter inside my controller.
The only problem is when I use it like so, the console log throws an error that says $filter is not a function.
app.controller('myController', ['$scope', '$http', '$filter', function($scope, $filter, $http) {
When I swap them round it throws an error that $http is undefined
When you are using
app.controller('myController', ['$scope', '$http', '$filter', function($scope, $filter, $http) {
variable $filter is actually a instance of $http, and $http is instance of $filter. Actually it doesn't matter what is written in function(...) params.
What is important here, is the order of injectibles you are using, for example
app.controller('myController', ['$scope', '$http', '$filter', function(a, b, c) {
will map to instances:
a -> scope
b -> $http
c -> $filter
From angular docs:
Since Angular infers the controller's dependencies from the names of arguments to the controller's constructor function, if you were to minify the JavaScript code for PhoneListCtrl controller, all of its function arguments would be minified as well, and the dependency injector would not be able to identify services correctly.
So by using array notation for yout controller, you are making sure that code will work after code is minified.
add filter after http and angular version also defends how you are
going to use filter.
plateFormController.$inject = ['$scope', '$http',
'$filter','$timeout', '$q', '$mdSidenav', '$log'];
function plateFormController($scope, $http,$filter, $timeout, $q) {
jsonByName=$filter('filter')($scope.json, { name: 'a' });
}
In my case, expected data List object is null, by ensuring that expected List Object not null. I am able to avoid this error.

Categories

Resources