I am working with AngularJS and have created a Module, which has a Factory and a Filter. The Factory gets a local json file translations) and the filter provides a function that returns a translated version of the text. So the code looks like the following;
angular
.module('i18n', [])
.factory('translationDataFact', ['$http', function($http){
var t = {};
var user = {};
t.defaultLanguage = 'en-GB';
t.languageFile = null;
t.init = function(){
t.setLanguage();
if(!t.languageFile){
$http
.get('translations/' + t.defaultLanguage + '.json')
.success(function(data){
t.languageFile = data.strings;
})
.error(function(error){
console.log(error);
});
}
}
t.setLanguage = function(){
/* change default language to User language here */
if(user.id){
t.defaultLanguage = user.language;
}
return t.defaultLanguage;
}
t.init();
return t.languageFile;
}])
.filter('t', ['translationDataFact', function (translationDataFact) {
var translate = function (stringIdenitfier) {
var translation = translationDataFact.languageFile[stringIdenitfier];
if(translation){
return translation;
}
return "translate me!!";
};
return translate(stringIdenitfier);
}]);
Then I wish to use the filter to translate variables and names like this
{{"string" | t }}
The problem I am having is that I have no idea how to make sure
The return of the Factory is set before the Filter runs this.
Also I am confused by how I prevent the whole application rendering until this filter is ready?
Any help would be amazing as I am lost :(
Is there a reason why you don't use an existing angularjs translation library like angular-translate?
In factory method u need to return Service itself, not result of operation. (I am not sure what exactly u want from this serivce)
when you return t.language it is always null and it will remain null in your filter... because your http call is asynchronious.
I would make this like:
app.module('translationDataFact', ['$resource', function($resource) {
var t = {};
t.init = function() {
t.result = $resource('...');
}
t.init()
return t;
}]);
In controller you have:
$scope.language = translationDataFact.result;
You make filter with parameter, inside filter you can check whether language is undefined or not.
So later you write:
{{ "string" | t:language}
And after language 'arrives' you see translation.
To answer your concerns:
Your factory should return something that can be asked for a specific translation. If the translations are not ready just return something basic like an empty string or null. e.g.
return translations.t(languageFile, translationKey);
Where t() would be a function that inspects the internal data structure of translations and can return either the result of the translation or the value mentioned earlier if the translations haven't been loaded yet.
You can do something like ng-show="translations.isLoaded()" on your top level element, but you'd need to set up a reference to the translations service on the $scope of your highest level controller. You may want to do this on the $rootScope so your translation service is always available in controllers as well.
Related
confirm("Ohhh, hello there, is it Ok to click Cancel?");
I think that this is, basically, a question about CRUD on Angular. I'm kind of confused about getters and setters, mainly because Angular do almost all the job in getting and setting things because of its two way data binding. I want to know what's the best scalable way to create getters and setters so I wont need to modify my functions in the future.
On the first Arrangement, I'm trying to be as simple as I can be, but I feel uncomfortable in getting and getting to set.
Arrangement 01:
$scope.getData = function(){
$http.get(url + '/data')
.then( function (res) {
return res.data; } );
};
$scope.setData = function () {
$scope.data = $scope.getData();
};
$scope.insertData = function (data) {
$http.post(url + '/data', { data: data})
.then( function (res) {
// nothing here. } );
};
On this second Arrangement, however, I'm trying to go directly where I need to. When I fetch data from the server, I'm automagicaly setting my $scope.data to the retrieved data;
Arrangement 02:
$scope.getData = function () {
$http.get(url + '/data')
.then( function (res) {
$scope.data = res.data;
});
};
$scope.insertData = function (data) {
$http.post( url + '/data', { data: data })
.then( function (res) {
$scope.getData(); //To update.
//OR $scope.data.push(res.data);
});
};
Looking further, I've found this on the Angular Docs, but what's the point in using a getter/setter if Angular already do it? Looking into other technologies, it's hard to compare, because Angular has auto-get.
I don't even know how to formulate this question. But, basically, I want to know how could my getters and setters harm my future application and if there's a good way and why to create getters and setters in Angular.
Thanks for any advice.
You good practice is to wrap your logic into Service. You have to know that in Angular, all services are Singleton, there is only a single instance of a Service.
I've made a simple example, by using $q.defer() which is the promise manager from the deferred API.
$q.defer() get 2 methods :
resolve(value) : which resolve our associated promise, by giving her the final value
reject(reason) : which resolve an promise error.
Controller
(function(){
function Controller($scope, $q, Service) {
//Use promise manager
var defer = $q.defer();
///Create our promise
var promise = defer.promise;
$scope.data = [];
var newData = [
{
name:'john',
age: 25
},
{
name: 'toto',
age: 13
}
];
Service.get().then(function(data){
//Retrieve our data
$scope.data = data;
//Set new data to our factory
Service.set(newData);
//Retrieve new data
Service.get().then(function(data){
//Resolve new data
defer.resolve(data);
});
});
//Retrieve new dataset
promise.then(function(data){
$scope.data = data;
})
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service($q){
var data = [0,1,2,3,4];
function set(value){
data = value;
}
function get(){
return $q(function(resolve){
//Simulate latency
setTimeout(function(){
//Resolve our data
resolve(data);
}, 1000);
});
}
return {
get: get,
set: set
};
}
angular
.module('app')
.factory('Service', Service);
})();
HTML
<body ng-app='app' ng-controller="ctrl">
<pre>{{data}}</pre>
</body>
So, you can set some data by using the service, and retrieve it when you want. Don't forget that service is singleton.
You can see the Working Plunker
In JavaScript you typcially don't use getters and setters like in OOP languages, especially because you do not have a notion of privateness (so anyone can access your fields). ES5 has getters and setters, but it also adds this missing capabilities of hiding implementation details. In case you want getters and setters for additional logic in your AngularJS app, you could simply define additional fields which are updated using $watch.
Furthermore you solution with sending an HTTP request on every change is a it of an overhead if you do this per field. What you instead to is writing directly to fields.
While e.g. WPF/C# requires you to define setters to raise OnPropertyChanged, you don't need this in AngularJS. Everything that you write in AngularJS will automatically trigger a so-called $digest cycle, where it checks for changes that have been made. It will then automagically update your user interface, give that you use template bindings or ng-model directives.
If you think like pure Javascript, is basic the same logic, what angular does is create modules for you to use the best practice, so it is easy to use them.
function DataService($http) {
this.get = function() {
return $http.get(...);
}
this.create = function(newData) {
return $http.post(...);
}
..
}
and using angular, like Ali Gajani sayd, you basically can do this,
angular.module('myApp').service('DataService', ['$http', DataService]);
or with a factory style
function DataService($http) {
var myPrivateVariable = "something";
function get() {
return $http.get(...);
}
...
// expose them public
return {
get: get
};
}
angular.module('myApp').factory('DataService', ['$http', DataService]);
I'm trying to retrieve a list of options from our database and I'm trying to use angular to do it. I've never used services before but I know that's going to be the best way to accomplish what I want if I'm going to use data from my object in other controllers on the page.
I followed a couple tutorials and put together a factory that makes an http request and returns the data. I've tried several ways of doing it, but for some reason nothing is happening. It's like it never runs the factory function and I can't figure out why.
Factory:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {};
locaService.locations = {};
var resorts = {};
locaService.getLocations=
function() {
$http.get('/url/url/dest/').success(function (data) {
locaService.locations = data;
});
return locaService.locations;
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
$scope.resort;
$scope.locations= locaService.getLocations();
}]);
I just want the data to be returned and then assigned to the $scope.locations to be used for ng-options in the view. Then I want my other function to run on click for the next field to be populated by the variable resort. How would I do this? Any help would be great! Thanks!
$http service returns a promise, and your function should return that promise. Basically your getLocations function should be something like the following
locaService.getLocations=
function() {
return $http.get('/url/url/dest/');
};
Then in your controller you should retrieve the options using this promise:
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
Using jquery in controllers or manipulating dom elements in controllers is not a good practice, you can apply styles and css classes directly in views using ng-style or ng-class.
Here is an example how all it should look wired up:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {
locations: {}
};
var resorts = {};
locaService.getLocations= function() {
return $http.get('/url/url/dest/');
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
/* Apply these styles in html using ng-style
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
*/
$scope.resort;
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
}]);
I'm having some basic problems with angular at the moment. I just wrote a service that reads the temperature of an external device in an interval of five seconds. The service saves the new temperature into a variable and exposes it via a return statement. This looks kind of this (simplified code):
angular.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = 0,
requestTemp = function() {//some logic here},
onResponseTemp = function (temp) {
m_temp = temp;
},
//some other private functions and vars ...
foo = bar;
//request new temperture every 5s, calls onResponseTemp after new data got received
$interval(requestTemp, 5000);
return {
getTemp = function(){return m_temp;}
}
}]);
I use a controller to fetch the data from the service like this:
angular.controller("tempCtrl", ["$scope", "tempService", function ($scope, tempService) {
$scope.temp = tempService.getTemp();
}]);
In my view I access it like this:
<div ng-controller="tempCtrl">
<p>{{temp}}</p>
</div>
But I only get 0 and the value never changes. I have tried to implement a custom Pub/Sub pattern so that on a new temperature my service fires an event that my controller is waiting for to update the temperature on the scope. This approach works just fine but I'm not sure if this is the way to go as angular brings data-binding and I thought something this easy had to work by itself ;)
Help is really appreciated.
Please see here http://jsbin.com/wesucefofuyo/1/edit
var app = angular.module('app',[]);
app.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = {
temp:0,
time:null
};
var requestTemp = function() {
m_temp.temp++;
m_temp.time = new Date();
};
var startTemp = function() {
$interval(requestTemp, 3000);
};
return {
startTemp :startTemp,
m_temp:m_temp
};
}]);
app.controller('fCtrl', function($scope,tempService){
$scope.temp = tempService;
$scope.temp.startTemp();
});
You are returning a primitive from your service, if you want to update an primative you need to reftech it. You should return an object, as on object is passed by reference, you get the actual values in your controller.
do this in your service:
return m_temp;
And this in your controller:
$scope.temp = tempService;
and your view will update as soon as the service gets updated.
Does this help you?
i think you should use $interval in controller ot in service
$interval(tempService.getTemp(), 5000);
I've ended up with a lot of logic in my controller which I realise is not good. Therefore I would like to move this to a service.
At the moment the controller accepts a url which will either be from YouTube or Vimeo. It detects whether the string "youtube" or "vimeo" is present in the url and then does what it needs to do accordingly. Here's part of the "logic" that currently resides in the controller:
if url.indexOf("youtube") > -1 {
variable_1 = "Something";
variable_2 = "Something";
//do some more stuff
}
else {
variable_1 = "Something";
variable_2 = "Something";
//do some more stuff
}
$scope.task.items.push("I need to add things to this array too");
A Service is the way to go but my first question is a service or a factory?
This is what I'm working on but I'm not sure how I would pass the variables that exist in the controller (variable_1 and variable_2) back to the controller when the service has completed.
myApp.service('urlService', function() {
this.detectProvider = function() {
if url.indexOf("youtube") > -1 {
}
else {
}
//how can I push things to the $scope array here?
};
});
In your service
myApp.service('urlService', function() {
this.detectProvider = function(url) {
arrayOfMyVars = new Array();
if url.indexOf("youtube") > -1 {
arrayOfMyVars.push("Something");
arrayOfMyVars.push("SomethingElse");
}
else {
arrayOfMyVars.push("Something");
arrayOfMyVars.push("SomethingElse");
}
//how can I push things to the $scope array here?
return arrayOfMyVars;
};
});
in your controller
var res = urlService.detectProvider(url);
variable_1=res[0];
variable_2=res[1];
$scope.task.items.push('the thing you need to push'); // maybe res[2] and in your service you had another arrayOfMyVars.push('the thing you need to push')...
Don't forget to import your service into your controller ;)
A Service is the way to go but my first question is a service or a
factory?
Simply speaking it does not matter. From personal point of view just use what suits you best. A service will create a new instance of the function, a factory will simply execute the function and do not create a new instance of it.
About your second question: Simply return variable_1 and variable_2 in your services method and assign them to your $scope.
myApp.service('urlService', function() {
this.detectProvider = function(url) {
if url.indexOf("youtube") > -1 {
...
return [variable_1, variable_2];
}
else {
...
return [variable_1, variable_2];
}
};
});
Service or Factory both are singleton instances. At the end u will get object only.
In service you will create using functtion constructor
In Factory we can construct it using object literrals
I have already started to rework this code to operate synchronously, but out of curiosity and a desire to support both means, I need some help understanding how to get a filter to jive with a promise. As some other posts mention a filter seems to just resolve to {} from a promise.
Basic Pattern
Here's a breakdown:
Define a service in the module that returns a promise instead of an object
module.factory('promisedSvc', ['$http', function($http) {
var httpPromise = null,
servicePromise = null,
service = {},
dataSet = {};
var httpPromise = $http.get('somedata.json').success(function(data) {
dataSet = data;
});
servicePromise = httpPromise.then(function(){
service.getData = function(key) {
return dataSet[key];
};
service.addData = function(key, value) {
dataSet[key] = value;
};
return service;
});
/*
In actuality I proxied the service methods onto the promise because
I didn't want consumers of the service to have to deal with it being
a promise. There is the caveat of setting properties on a class I
don't own (property collisions), a risk I'm okay taking, but YMMV
Commented out proxies
servicePromise.getData = function(key) {
return this.then(function(svc){
return svc.getData(key);
});
};
servicePromise.addData = function(key, value) {
this.then(function(svc){
svc.addData(key, value);
});
};
*/
return servicePromise;
}]);
Controllers can handle this promisedSvc fine, you just get the promise injected into the controller and then use the then function on the promise to wrap the setting of a $scope property to the function call on the eventual service object: getData(key) or setData(key, value). Alternately you can just treat it as normal if you proxied the functions onto the promise like in the commented out block.
Filters do not seem to inherently handle promises like $scope does. I am looking for a way to get the filter to inject the promisedSvc and be able to call getData(key) without it resolving to {} because the promise has not resolved yet. Below is an example of what does not work:
module.filter('svcData', ['promisedSvc', function(promisedSvc) {
return function(input) {
return promisedSvc.then(function(svc) {
var value = svc.getData(input);
return value;
});
};
}]);
So is there a way to write the filter to be able to resolve the value?
Use Case
That is the simplified pattern of what I am trying to achieve. For those curious, my actual use case is to pre-fetch i18n/l10n resource bundle information so I can localize all the text in my application. The pre-fetch could all be in the Javascript environment (attached to some already loaded global or in a provider), but we also have scenarios with database-stored Resource Bundles so I needed a version of code that can pre-fetch all the information from the server via AJAX.
It's not exactly what I'm looking for, but at least to document a workaround:
It's possible to use a function on the $scope instead of a filter.
module.factory('promisedSvc', ['$http', '$rootScope', function($http, $rootScope) {
var httpPromise = null,
servicePromise = null,
service = {},
dataSet = {};
var httpPromise = $http.get('somedata.json').success(function(data) {
dataSet = data;
});
servicePromise = httpPromise.then(function(){
service.getData = function(key) {
return dataSet[key];
};
service.addData = function(key, value) {
dataSet[key] = value;
};
//Here is the addition to setup the function on the rootScope
$rootScope.svcData = function(key) {
return service.getData(key);
};
return service;
});
return servicePromise;
}]);
And then in a template instead of {{ 'key1' | svcData }} you would use {{ svcData('key1') }}
I tested that if you delay the promises resolution (for example setup a wait in the $http.success) that the impact is the page loads, but the values from the svcData function will only populate into the template once the promises resolve.
Still would be nice to accomplish the same with a filter if possible.