Angular Translate - how to retrieve dynamic variable list - angularjs

I have this in the translation json file:
{
"test_key" : "Var1: {{var1}} Var2: {{var2}} Var3: {{var3}}"
}
For this to work, I need to provide var1, var2, and var 3. For example:
$translate('test_key', { var1: 1, var2: 2, var3: 3 });
The issue now is that var1, var2, var3 could be any dynamic variables. What I need now is to get all the list of dynamic variables so I can provide whatever values it may need.
Ideally, this is the goal I am trying to achieve (pseudo code)
var dynamicVars = getDynamicVarList(); // I need this, but how?
dynamicVars.forEach(function (key) {
switch (key) {
case "var1":
return 1;
case "var2":
return 2;
case "var3":
return 3;
default:
return null;
}
});

If it is Pascal Precht translate, then you set JSON file in module options, before application init. It's rather not possible with standard mechanisms. It offer JSON, but when it is loaded, then it's hard to change something, without changing source code of angular translate module.
If the reason you want this is to have many languages, then you can set many languages codes in $translate.
Another solution is to load JSON from server, which performs operations on var1, var2, var3 and hence returns static json, but you can do $http calls with commands to change variables in switch statement.
It looks somehow linguistic approach, Java is good for this. Grails may be fine framework for returning REST services.

Again, to restate the problem, the issue is that you do not know ahead of time which dynamic variables are to be used.
I solved the issue by using a customer interpolator.
So when you do
{{'testkey'|translate}}
and your lang.json has:
"testkey" :"this is number {{variable1}}"
It will get resolved to
this is number 1
Code:
app.factory('customTranslateInterpolator',
["$interpolate",
function ($interpolate) {
var $locale;
var customTranslateInterpolator = {};
/**
* This will be your custom dynamic vars resolution method
*/
var resolveExpressions = function (expressions) {
var values = {};
expressions.forEach(function (key) {
values[key] = resolveVariable(key);
});
return values;
}
/**
* The actual method for key:value mapping
*/
var resolveVariable = function(key) {
var retVal;
switch (key) {
case "variable1":
retVal = 1;
break;
default:
retVal = "";
}
return retVal;
}
customTranslateInterpolator.setLocale = function (locale) {
$locale = locale;
}
customTranslateInterpolator.getInterpolationIdentifier = function () {
return 'custom';
},
/**
* Custom interpolate
* interpolateParams will overwrite resolve expressions. This will allow developers
* to pass values to the translate directives or by using $translate service.
*
* #param {string} string the string retrieved from the language json file
* #param {object} interpolateParams the {key:value} object passed to the angular-translate features.
*/
customTranslateInterpolator.interpolate = function (string, interpolateParams) {
var result;
interpolateParams = interpolateParams || {
};
// Retrieve expressions and resolve them
var interpolatedString = $interpolate(string);
var resolvedExpressions = resolveExpressions(interpolatedString.expressions);
// Merge interpolateParams onto resolvedExpressions so that interpolateParams overwrites resolvedExpressions
angular.extend(resolvedExpressions, interpolateParams);
result = interpolatedString(resolvedExpressions);
return result;
}
return customTranslateInterpolator;
}]);
make sure you let angular-translate know you are using a custom interpolator
$translateProvider.useInterpolation('customTranslateInterpolator');
Note that you can still provide your own translate-values, and this will override whatever you have in resolveVariable()
So if you do
{{'testkey' | translate:"{variable1:'2'}"}}
It will resolve to
this is number 2
Hope this helps others.
-Lucky M.

Related

Detection of Index in Angular: indexOf() does not work properly because of $$hasKeys?

Following Situation:
role: { roleid=3, name="admin"}
availableRoles:
[
{ roleid=3, name="admin", $$hashKey="object:222"},
{ roleid=4, name="plain user", $$hashKey="object:223"}
]
currentRoles:
[
{ roleid=3, name="admin"}
]
Following Trys:
currentRoles.indexOf(role); // works properly and outputs 0
availableRoles.indexOf(role); // does not work
I can imagine, this occurs because of $$hasKeys. But I didn't put them there, AngularJS does augment these data.
How can I overcome this situation?
Is there a function like: ignore Angular HasKeys in this Datastructure?
Edit:
Angular object comparison:
Compare objects in Angular
So you can just write the function:
function arrayObjectIndexOf(arr, obj){
for(var i = 0; i < arr.length; i++){
if(angular.equals(arr[i], obj)){
return i;
}
};
return -1;
}
--ORIGINAL--
JavaScript saves objects as pointers, therefore, two objects even if has the same data in them, have different values (the value of the pointer in the memory).
Code example:
var role = { roleid:3, name:"admin"};
var availableRoles =
[
{ roleid:3, name:"admin"},
{ roleid:4, name:"plain user", $$hashKey:"object:223"}
];
alert(availableRoles.indexOf(role));
http://codepen.io/anon/pen/BjobaW
So it does not relate to the hashKey. To compare to objects (and such, find the index in an array) you must create a loop of comparison, or overload the "==" operator of Object to compare values and not pointers, which I dont believe you are allowed to do in JS.
Best way is not to have such objects...
You can use angular filter:
function contains(arr, id) {
return $filter('filter')(arr, {roleid : id}, true).length != 0;
}
You can use some other js library (lodash, underscore, ...) for such things.

Strongloop promise inside loop

I am trying to call a loopback find function inside of a for loop, passing in a value from the iteration into the loopback function. The main issue of the code can be represented by the following:
for (var a = 0; a < $scope.countries.length; a++) {
$scope.getEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e').then(function(result) {
emFacPurElecToUse = $scope.emFacPurElecs;
}
And here is the function being called:
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
$scope.emFacPurElecs = [];
$scope.emFacPurElecs = Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
});
defer.resolve('Success getEmFacPurElec');
return defer.promise;
};
The problem is that the loopback promise function is called and then returned undefined which means that it moves to the next iteration of the for loop before getting the value to assign to emFacPurElecToUse. I need to do some more calculations with that variable for that country before moving to the next country.
I have looked at using $q.all as a possible solution and also using array.map as per http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html (Rookie mistake #2: WTF, how do I use forEach() with promises?), but I just cannot figure out how to pull it all together to make it work. Should I be using a forEach instead?
I also saw this link angular $q, How to chain multiple promises within and after a for-loop (along with other similar ones) but I do not have multiple promises that I need to process inside the for loop. I need to retrieve the value of one emFacPurElecs for that country, do some work with it, then move to the next country. I feel I am close but I just cannot get my head around how I would code this particular functionality. Any help is greatly appreciated.
It seems to me that you do have multiple promises to process inside your for loop, as you say "I need to do some more calculations with that variable for that country before moving to the next country." This should all be done with in the promise chain that I've suggested - calcEmFacPurElec.
$scope.calcEmFacPurElec = function (country, unit, ghgType) {
$scope.getEmFacPurElec(country, unit, ghgType).then(function(countryEmFacPurElecs) {
// do something with countryEmFacPurElecs
return countryEmFacPurElecs;
}
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
defer.resolve(Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
}); );
return defer.promise;
};
Hopefully the above is a pointer in the right direction!
When you want to carry out a promise chain on an array of items, then as you have identified, Promise.all (using whatever promises implementation you require) is what you want. .all takes in an array of Promises, so in your for loop you can do:
var promises = [];
for (var a = 0; a < $scope.countries.length; a++) {
promises.push($scope.calcEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e')); // new promise chain that does all of the work for that country
}
$q.all(promises).then(function(arrayofCountryEmFacPurElecs) {console.log('all countries completed')});

Re-use array collection on multiple pages

I currently have 2 pages, page1.php and page2.php, each of the pages uses a controller that completes its own functions etc.
However there are tabs within the pages that are exactly the same that gets a promise from a factory within the module. The lists are exactly the same except for querying on different IDs. For example both controllers have this:
pageListFactory.getData().then(function (result) {
$scope.BasicItems = result; $scope.basicItemsList = [];
angular.forEach($scope.BasicItems, function (BasicItem) {
angular.forEach(BasicItem['SomeInnerArray'], function (BasicSomeInnerItem) {
if (BasicSomeInnerItem == VARIABLE_THAT_CHANGES) {
$scope.basicItemsList.push({
ID: BasicItem.ID, Title: BasicItem.Title
});
}
});
});
});
So this code is used, and the VARIABLE_THAT_CHANGES is just what changes each time. However as I said it is used on multiple pages, is there a way to create just one function call and then each page just can call a specific bit and send the variable with it?
I tried using $rootScope but of course this just clogs and slows, and I'm not exactly happy on constantly using $rootScope to pass the $scope.basicItemsList around as the list could get quite big.
So is there any way to reuse this code without just copying and pasting it into each controller?
Sure you can re-use it...
Convert the factory to a service, its basically a name change, create a local variable to store the data, update the data on first call, and then grab the data if it exists on the second call.
.service('myService', ... stuff ... { // I suggest using a service, as I don't know if a factory would act as a singleton
var myData = null;
return {
getData: function(){
if(myData != null)
return myData; // returns data
else {
return $http()... // ajax call returns promise
}
},
setData: function(dataToSet){
myData = dataToSet;
}
}
Then your controllers:
//controller 1
var promiseOrData = pageListFactory.getData();
if(promiseOrData instanceOf Array){ // or whatever data you expect
$scope.BasicItems = promiseOrData;
....
}
else { // should be a promise
promiseOrData.then(function (result) {
pageListFactory.setData(result); // set the data once you get it.
$scope.BasicItems = result; $scope.basicItemsList = [];
....
}
}
In controller 2 you only need to get the data as the returned data will be an array, not a promise
On top of all this, write a directive which will process the data when you pass it along, then you can pass the variableThatChanges and have the directive take care of it.
Use services and write the function in that service and pass the variable VARIABLE_THAT_CHANGES into it. By this you can reuse the code.

Angular resource REST has no method '$save'

I would like to interact with a REST API using $resource but when calling the save method I get has no method '$save' error. My code is inspired by the answer found on AngularJS $resource RESTful example
myapp.factory('Monitoring', function($resource) {
return $resource('http://localhost:8080/wepapp/network/v1/cronjobs/:id', { id: '#id' } );
});
Q1: What is the purpose of # in { id: '#id' }? I found it in most of the examples.
myapp.factory('MonitoringCRUDControllerService', ['Monitoring', function(Monitoring) {
return {
create: function(id, command, schedule) {
console.log("create");
console.log(command);
console.log(schedule);
Monitoring.id = id;
Monitoring.command = command;
Monitoring.schedule = schedule;
console.log(Monitoring);
Monitoring.$save();
}
}
}]);
The Monitoring object is correctly injected:
function Resource(value){
copy(value || {}, this);
}
Calling the $save failed with the error has no method '$save'.
Q2: What the purpose of $ before save?
Q3: What am I missing to make the save method work?
You need to create an instance of your Monitoring class like this (not tested):
var m = new Monitoring({id:id});
m.command = command;
m.schedule = schedule;
m.$save();
The documentation (http://docs.angularjs.org/api/ngResource.$resource) has a similar example:
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
I asked the same question about the # sign a while ago: "at" sign in parameter names in resource definition. Basically # sign means that the value will be read from the object's property. Or as the same documentation says:
If the parameter value is prefixed with # then the value of that
parameter is extracted from the data object (useful for non-GET
operations).
The $ is not any special character, it is just a part of the method name.

no of records in a store are not returning in extjs

I am new to extjs and was working on creating a dynamic screen depending on the no of records from the Ext.data.store(); getTotalCount/getCount are used to get the no of records from the store. I need to store the total no of records in a var and return it
I am trying to do something like this
function Get_count()
{
var num;
CacheStore.on({
'load':{
fn : function(store,records,options){
num = getTotalCount();
//console.info('Store count = ', tsize);
//console.info(' count = ', getCount());
},
scope: this
},
'loadexception' : {
fn : function (obj,options,response,e){
//console.info('error = ', e);
},
scope : this
}
});
// this is a wrong logic but have to do something similar
//return num; //return num
};
tsize = Get_count();
I always get null in tsize. I also tried getCount() instead of getTotalCount() butI am getting the same problem.
dont know where I am going wrong
Your logic is a bit poked here. You can't fire a function that will add a listener to a store that will hook when the store has finished loading. ( well you can, but this is a subtle bug down the line ).
What you need to do is declare a listener on the store when you create it, that contains the function you wanna use the number in.
cacheStore =Ext.create...
cacheStore.on('load',function(store,records,e){
//dosomestuff that needs the count
var num= store.totalCount()
//now you use the num in here, else you create an async error
//or you can ...
my.someFunc(num);
//in here, but you can only run it after the store has loaded
},this);

Resources