I'm using angular-translate module and am trying to inject all my translations that are on server with $http. I use a provider and I know that only i can inject dependencies through $get but I can't call that function from my provider. I need to know if i can do this and how i do it.
This is my provider.
.provider('languageServices', function (){
this.languages = {};
this.getExistLanguages = function() {
return ['en','es'];
};
this.getAllLanguages = function(){
return this.languages;
};
this.$get = function($http){
return {
getSpecificLanguage : function(lan) {
return this.languages = $http.post('fr3/i18n',lan);
}
}
};
});
this is my config app
.config(function ($stateProvider, $urlRouterProvider, USER_ROLES, $translateProvider, languageServicesProvider) {
$stateProvider.state('dashboard', {
url: '/dashboard',
views: {
'header': {template: ''},
'content': { templateUrl: 'views/dashboard.html' }
},
data: { authorizedRoles: [USER_ROLES.admin] }
});
$translateProvider.preferredLanguage('es');
// here is where i want inject all my translations with something like:
// var languages = languageServicesProvider.getAllLanguages();
//and languages pass it to translateProvider
});
I know this code has some errors but I only want you have a idea that I want to do.
Thanks
So angular-translate provides it's own $http process to do this. I happened to literally just implement this today, so you're in luck. You can see it here in their docs.
http://angular-translate.github.io/docs/#/api/pascalprecht.translate.$translateUrlLoader
Their docs are pretty bad, but the way you would implement this is in your app.config where you have your preferred language thing you would add...
$translateProvider.useUrlLoader(options)
Again this is where their documentation is bad. The options you need for this will just be your url, so...
$translateProvider.userUrlLoader({
url: '/yoururl'
});
This will create an http call that will try to get from '/yoururl?lang=en_US' or whatever language code is currently active.
You are also going to need to include the script for url loader which is this here
https://github.com/angular-translate/angular-translate/blob/master/src/service/loader-url.js
That also gives you some more info in the comments about using it.
Let me know if you have anymore questions. Hope this helps. Took me a while to figure out what is going on with this thing. Again, very bad documentation.
Edit:
I noticed you are also writing your own services to make a list of available languages. Angular-translate also has something for this...
$translateProvider.registerAvailableLanguageKeys(['en', 'ja']);
Related
I am working on an application that photographers can use to upload photos. The frontend is AngularJS and there is a RESTfull api backend.
Because of some issues and the fact that ui-router seems better then ngRouter, I decided to change the $routeprovider to $stateProvider of ui-router.
However, my resolve doesn't work anymore (I guessed it would break but I cannot find the solution to my situation).
So here is the original $routeprovider code:
.when('/photographer', {
templateUrl : '/static/partials/photographer/photographer_dash.html',
controller : 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos();
}
}
})
The PhotogService is a $resource service that has the following $resource objects:
return $resource(apiHost, {}, {
'ownPhotos': {
url: apiHost + '/photographer_own_photos/',
method: 'GET',
interceptor: ResponseInterceptor
}
});
In the controller I would then do the following (photogPrepService being injected because of the resolve):
var promise = photogPrepService;
promise.then(
function (data) {
$scope.ownPhotos = data.data.photos;
});
This all worked well and I would get the photos in the scope.
However as said with ui-router it doesn't work and I cannot seem to get it working...
According to the docs (https://github.com/angular-ui/ui-router/wiki#resolve) the following should work:
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos();
}
}
However, when the resolve is injected in the controller and I use console.log() to print the response, I get the following:
Resource(value)
I somehow cannot seem to get the values (JSON response {"photos": ...}) injected into the controller.. I tried various solutions that have been suggested here on stackoverflow and read guides and the API of ui-router, but I cannot wrap my head around what is going wrong... I hope someone can guide me in the right direction..
Thanks!
I think you can use the code below:
//the PhotogService will keep the same as before
//inject the 'PhotogService' into the controller photographerController
PhotogService.ownPhotos().then(function(data) {
$scope.ownPhotos = data.data.photos;
});
//instead of injecting the photogPrepService through 'resove', inject the PhotogService into the controller
//for the $stateProvider
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
}
You need to give a promise to the resolve, your ressource should look more like :
$stateProvider.state('photographer',
{
url: '/photographer',
templateUrl: '/static/partials/photographer/photographer_dash.html',
controller: 'photographerController',
resolve: {
photogPrepService: function (PhotogService) {
return PhotogService.ownPhotos().$promise;
}
}
});
Or modify your ressources to make them promises.
Probably it's just as easy as I think it is, but I cannot really find an answer to my question on the internet, so I hope you guys know the answer just by looking at a small piece of my code.
Problem: I'm using the UI router in Angular and it loads the template before all the data is loaded. So all input fields receive the correct values AFTER the template is already loaded. So the input fields are empty for a second or two....
I think my resolve is not as it should be:
So my ui-router code looks something like this (check the resolve object):
$stateProvider.state('teststate', {
url: '/test/',
templateUrl: 'app/page/template.html',
controller: 'testCtrl',
resolve: {
access: ["Access", function(Access) { return Access.isAuthenticated(); }],
UserProfile: 'UserProfile'
}
});
Now the controller contains the promise to get some data from an API url:
function TestCtrl($scope, $state, $stateParams, TestService) {
TestService.get($stateParams.id).then(function(response) {
$scope.data = response;
});
}
Now the service (which connects to the API) should return the promise to the Controller:
TestService.factory('TestService', ['Restangular', function(Restangular) {
var factory = {};
factory.get = function(id) {
return Restangular.one('api/test', id).get();
}
return factory;
}]);
Now, could the problem be, that because the TestService.get() (which connects to the API) within the Controller, gets executed NOT before the template is loaded, because it's not inside the resolve object? So the UI router doesn't resolve the call to the API? I'm just curious or I should move all methods which make API calls, to the resolve object of each stat inside the $stateProvider.
I could run a lot of tests, but if someone just directly knows the answer by just looking at this question, it helps me a lot.
Your assumptions are all correct.
If you resolve the TestService.get in routing config the data would be readily available to controller as an injectable resource
If you don't want your controller to run and your template to show before all your API calls are finished, you have to put all of them inside ui-routers resolve.
However, if API requests can take a little while it seems better UX to transition to the new page immediately and show some kind of loading indicator (e.g. block-ui) while your API call is running.
I'm trying to perform a simple test on a helper function I've created:
function getPostId() {
return $stateParams._id;
}
And here is the test I am using, please note that getService is simply a wrapper for the Angular inject() function:
describe('wpApp Router', function() {
var $state, $stateParams, Router;
beforeEach(module('wpApp.core.router'));
beforeEach(function() {
$state = getService('$state');
$scope = getService('$rootScope');
$stateParams = getService('$stateParams');
Router = getService('Router');
$templateCache = getService('$templateCache');
$templateCache.put('/app/sections/posts/list/post-list.html', '');
});
it('should provide the current post ID', function() {
$state.go('posts.details', { _id: 1 });
$scope.$digest();
expect(Router.getPostId()).to.equal(1);
});
});
My router looks like this:
$stateProvider
.state('posts', {
url: '/posts',
controller: 'PostsListController as postsList',
templateUrl: '/app/sections/posts/list/post-list.html'
})
.state('posts.details', {
url: '/:_id',
controller: 'PostDetailsController as postDetails',
templateUrl: '/app/sections/posts/details/post-details.html'
})
I'm trying to set the current state to posts.details with an _id of 1. Then test my helper function to see if it is correctly retrieving the ID.
However, I am getting errors with the templates of the states, like the following: Unexpected request: GET /app/sections/posts/details/post-details.html
The state posts.details is a child of posts and both have their own templates, the problem is I am unable to load both into the templateCache or at least I can't figure out how to do so.
Is it possible to load both templates in order to fulfill the test?
In response to your actual question, the easiest approach to caching templates is by using something like karma-ng-html2js-preprocessor. It will cache all your application templates, and make them available to your tests as a module.
However, from what I can surmise from the code provided, this is un-necessary. Currently, your unit test is actually an integration test in that it's dependent on the routing of the 'posts.details' state in order to pass, and yet the method itself is concerned only with the $stateParams object.
So, instead, as $stateParams is in fact just a plain object, a simpler (and better) approach is just to mock it prior to your test:
beforeEach(module(function($provide) {
$provide.value('$stateParams', {
_id: 1
});
}));
and then you can test your service method in isolation from your states:
it('should provide the current post ID', function() {
expect(Router.getPostId()).toEqual(1);
);
I've created a nested resources in my backend.
Eg. /resource/:id_resource/subResource/:id_subResource
I'm new in angular.js and I'm trying to find the best way to do this.
I've used the services and angular-resource components and I need to understand how to pass the first part of the URL with the /resource/:id_resource/ in this correct format (not as normal parameter).
projectsServices.factory('SubResource', ['$resource','$location',
function($resource, $location){
return $resource($location.$$path , {}, {
query: {method:'GET', params:{}, isArray:true}
});
}]);
this is basically tied to the route
.when('/**resource**/:id_resource/**subResource**/:id_subResource', {
templateUrl: '/partials/[...].html',
controller: '[...]DetailsCtrl'
so the URL requested to the API is strictly tied with $location.
It works but I think is not the best way to do it.
Any thoughts about it?
I think you're looking for is just $routeParams - something like.
factory('SubResource', ['$resource', function ($resource){
return $resource('/api/:param_one/sub/:param_two',
{param_one:"#_id", param_two: "#id_sub"},
{
update: {
method: 'PUT'
}
});
}]);
Then in your controller
app.controller('Ctrl',function($scope, $routeParams
...
SubResource.get({
param_one: $routeParams.thingId,
param_two: $routeParams.thingId_sub
},
function(data){...});
This pull the params from your route
.when('thing/:thingId/sub/:thingId_sub', {
And after, if your query is your model you can just update if the parameters # matches.
SubResource.$update(function(res) {
$location.path('SubResource/' + res._id);
});
I will try to explain this as concisely as I can.
I have an app that can't be immediately converted to a single angular app. as a result we have a number of seperate modules that are effectively individual apps at the moment and are instantiated on their own relevant pages
Lets say we have two modules A + B both using angular-ui-router to manage state and views, they both access the same rest api but they display the information very differently. However to create or edit a resource in both A + B you use the same form.
This form has a template which can be shared easily but it also has a controller which right now is A.controller('formcontroller'). What is the best way (is there even a way) for me to take that controller away from the A module and inject it into both A + B?
some pseudo code to maybe explain what I am wondering a little clearer
angular.module('A', ['ApiService', 'ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('templateA', {
controller: 'templateACtrl'
})
.state('tempalteA.form', {
controller: 'templateAFormCtrl'
})
})
.controller('TemplateACtrl', function () {
})
.controller('TemplateAFormCtrl', function() {
});
angular.module('B', ['ApiService', 'ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('templateB', {
controller: 'templateBCtrl'
})
.state('tempalteB.form', {
controller: 'templateBFormCtrl'
})
})
.controller('TemplateBCtrl', function () {
})
.controller('TemplateBFormCtrl', function() {
});
I want to get rid of TemplateAFormCtrl and TemplateBFormCtrl and consolodate them into just FormCtrl that can be used by ui-router in both modules
Ok, read the edited example and it can be done in a couple of ways, the best one I can think of (and I will explain why before the solution) is this:
angular.module('formModule', [])
.service('formService', function(){
//Everything that can be abstracted from templateAFormCtrl should be here
});
angular.module('A', ['formModule'])
.config(function($stateProvider, $urlRouterProvider) {
//$stateProvider config
$stateProvider
.state('tempalteA.form', {
controller: 'templateAFormCtrl'
})
})
.controller('TemplateAFormCtrl', function(formService) {
//Assuming you're working with 'controller as' syntax, else attach to $scope
this.formService = formService;
});
angular.module('B', ['formModule'])
.config(function($stateProvider, $urlRouterProvider) {
//$stateProvider config
$stateProvider
.state('tempalteB.form', {
controller: 'TemplateBFormCtrl'
})
})
.controller('TemplateBFormCtrl', function(formService) {
//Assuming you're working with 'controller as' syntax, else attach to $scope
this.formService = formService;
});
Why I would do this? Yeah, you could create a controller on the 'formModule' instead of a service, but you won't be aware of the states of 'A' and 'B' from that controller, and if you later need to react to something that is set on either module or comunicate with any component from it (specially this, since you'll need to inject that component to the 'formModule' hence creating a recursive dependency), you'll have a huge problem.
The way I would do it, allows you to expose every method you want from 'formService' to your views, and on top off that, you'll be able to add code specific to either module on it's own controller.
OLD ANSWER:
The short answer would be:
A factory to manage everything related to API comunications (Requesting/Persisting data to the server)
A service to provide a common API for your controllers, that way you can abstract the methods you want to reuse, and use them on each controller by injecting the service as a dependency.
The longer answer (and I personally recommend you read it, because it will help you a big deal) is this: AngularJS: Service vs provider vs factory