angular translate update translation table - angularjs

I have created a directive which wraps angular-translate and can also turns into input fields for easy Translating for admin users.
When I update a single translation I don't really want to load an entire translation table from my DB after updating 1 row in the DB, because it seems incredibly inefficient.
My problem is that I can't seem to find anything in the angular-translate API that will allow me to have access to the front-end Cache. I want to modify the translation map directly without having to bother the DB for an entire mapping of my translations after I updated 1 row successfully.
Things that I have tried: $translationCache, $translateLocalStorage, $translateCookieStorage.
Can someone enlighten me please.
Also, as a bonus, I wonder if anyone figured out where they can expose the translation mapping in angular translate.
Note, I don't want the translated values from $translate in the controller because that's already interpolated.

A short view into the source offers no simple way for this. I solved it finally caching a reference from $translateProvider.translations using a own provider:
app.provider('translationHelper', function () {
this.translations = {};
this.$get = function () {
return {
translations: this.translations
}
};
});
In your app do
app.config([
'$translateProvider',
'translationHelperProvider',
function (
$translateProvider,
translationHelperProvider
) {
translationHelperProvider.translations = $translateProvider.translations();
}]);
and use it later like
app.component('myComponent', {
templateUrl: 'views/components/myComponent.html',
controller: [
'translationHelper',
function (
translationHelper
) {
// query translation
var translation = translationHelper.translations['de']["Your.Key.Here"];
// modify translation
translationHelper.translations['de']["Your.Key.Here"] = 'A new value';
}]
});
Alternatively, you can modify the angular translate source and made 'translations' from provider accessible through its $get method.

Related

Angular 1: $injector can't find a provider dependency when injecting provider inside a provider

My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo

Angular - reusing code (service or controller)

I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.

How to best organize translation strings in angular-translate?

I am using angular-translate on a rather large Angular project. I am breaking the project into multiple modules to make it more manageable, but I am unable to break up my translation strings per module.
For example, I have modules A and B, where B is a submodule of A. There are strings that pertain to the HTML covered by module A, which are placed in '/json/localization/A/en.json'. Likewise, there are strings pertaining to B that I place in '/json/localization/B/en.json'. First I load B's en.json in module B using angular-translate's $translationProvider. Then I load module A's en.json, also using $translationProvider. The problem is that loading A's strings overrides B's strings and they are lost.
Using angular-translate, is there a way to load strings per module, without overriding, or does the parent module have to load all of the strings from a single en.json?
Here is an example (in coffeescript) of how I am loading the translation strings:
my_module.config(['$translateProvider', ($translateProvider) ->
$translateProvider.useStaticFilesLoader
prefix: '/json/localization/A/'
suffix: '.json'
$translateProvider.preferredLanguage 'en'
])
angular-translate supports async loading of partial language files. All partials are merged into one dictionary per language.
The official documentation can be found here: http://angular-translate.github.io/docs/#/guide/12_asynchronous-loading
It supports applying a template for url templates that point to the modularised language files:
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: '/i18n/{part}/{lang}.json'
});
From within your controllers, you can add language modules and refresh the data bindings like this:
angular.module('contact')
.controller('ContactCtrl',
function ($scope, $translatePartialLoader, $translate) {
$translatePartialLoader.addPart('contact');
$translate.refresh();
});
Of course, loading the partials can also be covered in a route's resolve phase
Alternatively, you can also look into building your own custom loader function. http://angular-translate.github.io/docs/#/guide/13_custom-loaders
This provides all the flexibility you need to combine required language modules in one shot. E.g. you could do something like this:
app.factory('customLoader', function ($http, $q) {
// return loaderFn
return function (options) {
var deferred = $q.defer();
var data = {
'TEXT': 'Fooooo'
};
$http.get('nls/moduleA/en.json').success(function(moduleA){
angular.extend(data, moduleA);
$http.get('nls/moduleB/en.json').success(function(moduleB){
angular.extend(data, moduleB);
deferred.resolve(data);
});
});
return deferred.promise;
};
});

Service or provider with cached data

On a server side I have a json file in a dictionary form:
{
"term1": "definition1",
"term2": "definition2",
"term3": "definition3"
}
I'm trying to create a service or provider (one of them is sufficient) which will have a cache of a data from this json file and will be able to use it.
The structures look like:
myApp.service('translateSrv', function() {
this.dictionaryData; // how to populate
this.translate = function(input) {
return this.dictionaryData[input];
};
});
myApp.provider('translateProvider', function() {
this.dictionaryData; // how to populate
this.$get = function() {
return {
translate: function() {
return this.dictionaryData[input];
}
}
};
});
My question is how to populate a dictionary data in this service or provider before the first call of translate() method (in a time of module creation/configuration)? I can't do it asynchronously while first method call.
I want to use one of this structure, among others, in a filter:
myApp.filter('translate', ['translateProvider', function(translateProvider) {
return function(input) {
return translateProvider.translate(input);
}
}]);
I've started recently my work with Angular so maybe my approach is wrong. I will appreciate every hint.
Provider name:
Do not suffix your provider name with 'Provider' since the name you will use to inject the provider in config functions will already be suffixed with 'Provider'
myApp.provider('translate', /*...*/);
// -> injectable provider is 'translateProvider'
// -> injectable instance is 'translate'
Populate the provider in a config function:
myApp.config(['translateProvider', function(translateProvider) {
translateProvider.dictionaryData = { /*...*/ };
});
Advice for performance!
If your translations are static per page view, please consider pre translating your templates.
If you really need it, prefer writing the whole translation js object in an inline script in the document
1 XHR less
no lazy loading deferring application load
Lazy loading
If you really have to lazy load those translations:
Either defer application loading with an external XHR before application load, while keeping the translationData at the provider configuration level,
Or take advantage of the "resolve" part of angular rooting or ui-router, while setting the translationData object on the instance (not on the provider)
How to choose between both?
The first option is quite easy and you won't have to couple application routing with your lazy load constraint.
For the second choice, prefer ui-router and declare an abstract state responsible for handling the lazy loaded data in the "resolve" state property and make other states be children of this abstract state, so that you won't have to add resolve constraints on each state that have a dependency on translations.

Angularjs and Meteor "Session" reactivity, is there a way?

I'm trying to work with Meteor and Angularjs. I'm using Meteor_angularjs package, which works OK with Collections.
Now I'm trying to use Session and my reactive data store:
TestCtrl = [
"$scope",
function($scope){
$scope.value = Session.get('someValue');
}
]
This does not work.
QUESTION: Any suggestions on how to tie down Meteor's Session and Angular?
As far as I understand, I can write directive that will be polling Session every so ofter, however I don't think that's a good choice.
Thanks
UPDATE:
I've tried the following:
TestCtrl = [
"$scope",
function($scope){
Meteor.autorun(function(){
$scope.config = Session.get('testsConfig');
if (!$scope.$$phase){
$scope.$digest();
}
});
}
]
and it sort of works, however I get the following error:
Error: INVALID_STATE_ERR: DOM Exception 11
Error: An attempt was made to use an object that is not, or is no longer, usable.
at derez (http://localhost:3000/test:95:41)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30) angular.js:5526
$get angular.js:5526
$get angular.js:4660
$get.Scope.$digest angular.js:7674
(anonymous function) controllers.js:46
Meteor.autorun.rerun deps-utils.js:78
_.extend.run deps.js:19
Meteor.autorun.rerun deps-utils.js:78
_.extend.flush deps.js:63
_.each._.forEach underscore.js:79
_.extend.flush deps.js:61
_.each._.forEach underscore.js:79
_.extend.flush deps.js:60
UPDATE 2:
I've tried the service like this (might be wrong usage), still nothing. Now it doesn't update at all on Session value's changes.
Meteor.autorun(function(){
app.factory('ssn', function(){ return{
get: function(val){
return Session.get(val);
}
}});
});
TestCtrl = [
"$scope","ssn",
function($scope, ssn){
$scope.config = ssn.get('testsConfig');
}
]
UPDATE 3: Angular has $apply() for
to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries)
At the same time Meteor has Meteor.render() for
Most of the time, though, you won't call these functions directly — you'll just use your favorite templating package, such as Handlebars or Jade. The render and renderList functions are intended for people that are implementing new templating systems.
However, it seems like I just cannot put 2 and 2 together. :(
this as an old question with old answers but I see people referring to it so here is the updated answer.
First - there is a new library for angular-meteor that handles those cases for you.
And this library gives you two possible solutions:
If you want to bind a Session variable to a scope variable, use the $meteorSession service.
What it does is that every time the scope variable will change, it will change to Session variable (and trigger an autorun if it's placed inside one).
and every time the Session variable will change, the scope variable will change as well (and change the view that it's placed upon).
If you are using the Session variable just to get a variable reactive (meaning trigger an autorun), you should use getReactively . this just returns the already existing scope variable but trigger an autorun every time it changes. a good example of this can be found it our tutorial.
Note: In anyway, when you use Tracker.autorun inside Angular, you need to connect it to a scope. this can be easily done if you replace Tracker.autorun with the $meteorUtils autorun function
Hi here is an option (might not be the best but it works I think)
app.service('Session',function($rootScope){
var self = this;
self.objects = {};
self.get = function(name){
self.objects[name] = {"value" : Session.get(name)};
Meteor.autorun(function() {
var i = Session.get(name);
if(self.objects[name].value != i){
if (!$rootScope.$$phase){
$rootScope.$apply(function(){
self.objects[name].value = i;
});
}
}
});
return self.objects[name];
}
self.set = function(name,value){
self.objects[name].value = value;
Session.set(name,value);
}
return self;
});
Call it in the $scope like this
$scope.test = Session.get("test");
In the view as {{test.value}}. Sorry for the late answer .
Happy new year!
try
angular.module('yourmod', [])
.controller('TestCtrl', ['$scope', function($scope) {
var c = Deps.autorun(function (comp) {
//... put reactive stuf on scope.....
if(!comp.firstRun) {
// only do not do aply at first run becaulse then apply is already running.
$scope.$apply()
}
});
// and to realy make it nice...
$scope.on('$destroy', function () {c.stop()});
}])

Resources