I'm trying to build multilingual website in AngularJS. Delivering proper translation in templates seems pretty straightforward but I'm stuck on implementing proper multilingual routes using UI-Router. I couldn't have found any suitable examples.
It has to scale easily while adding new languages. Example of routes structure for two languages is:
/en
/en/products
/en/products/green-category
/en/products/green-category/green-product-1
/cs
/cs/produkty
/cs/produkty/zelena-kategorie
/cs/produkty/zelena-kategorie/zeleny-produkt-1
Product categories are hard-coded, products are loaded from database.
I'm trying something like this (leaving out product categories since they are similar to 'products', just one level deeper in hierarchy):
$stateProvider.state('product', {
url: '/{lang:' + Translate.getLangs() + '}/{products:' + Translate.getRouteVariants('products') + '}/{productSlug}',
templateUrl: 'app/product.html',
resolve: {
product : function($stateParams){
return Data.products.get( Data.products.deslug($stateParams.productSlug, $stateParams.lang), $stateParams.lang );
}
}
});
And in controller / template:
$scope.productLink = function(id) {
return {
lang:$rootScope.lang,
products:Translate.getRoute('products', $rootScope.lang),
productSlug:Data.products.slug(1, $rootScope.lang)
};
}
<a ui-sref="product(productLink(1))">Green product 1</a>
Where
Translate, Data are providers
Translate.getLangs() -> 'en|cs'
Translate.getRoute('products', lang) -> 'products' or 'produkty' based on lang
Translate.getRouteVariants('products') -> 'products|produkty'
Data.products.slug(productId, lang) -> returns productSlug from model
Data.products.deslug(productSlug, lang) -> returns productId from model
Data.products.get(productId, lang) -> loads data
Handling with current language should be done better (probably in Translate provider).
Routes shouldn't match cross-lang urls such as '/en/produkty'. Big problem.
/edit: I probably could use $stateChangeStart, check whether all parametres are in one language and if not, redirect to top-level state.
And this whole solution doesn't seem way too elegant (since I'm begginer with Angular) so if anyone can provide any insights on this subject, I'll be glad.
Thanks.
First I would like to advise about one particular point about displaying multi-lingual content if your presentation is going to be same irrespective of language and locale. And that is...
You do not want to repeat your view/presentation code.
Your code could duplicate quite a lot and cause you some problem.
However if you really want to have them separate template I am thinking that your following line may have problem;-
url: '/{lang:' + Translate.getLangs() + '}/{products:' + Translate.getRouteVariants('products') + '}/{productSlug}',
If you use curly brace approach the text after : is actually a regular expression.
So you can't just return the getLangs() as 'en|ch' it should be something like
'[a-z][a-z]{en|ch}'
(I have not tested but should work.)
Related
I am developing an application using .NET Core MVC. I have different data-sets that loads through request to the controllers. For example, I have this controller methods:
localhost/Election
localhost/Election/2008
localhost/Election/2008/United_States
And the methods that works for this parameters are in ElectionController like:
public async Task<IActionResults> Index(){
// Return a list of data
}
public async Task<IActionResults> Index(string year){
// Return a list of data
}
public async Task<IActionResults> Index(string year, string location){
// Return a list of data
}
What happens is, these methods return some data in the same view page / Election/ Index.
But I want to use different JavaScript codes and different html to represent this data-sets in the /Election/Index view page. Let's consider a scenario:
If user enters : localhost/Election Then application is Rendering some table and using /script/jsCode1.js
When user enters into: localhost/Election/2008 Then application is Rendering some cards and using /script/jsCode2.js
When user enters into: localhost/Election/2008/United_States
Then application is Rendering some charts and using /script/jsCode3.js
How should I approach to solve this problem?
you need to render then in different pages.You should not render all in one.
Ex: localhost/Election will render Table.Html with their respective js. You can return specific html for specific endpoint.
Then you will have uno html for one of your endpoints in controller.
Ok so... first of all, there are a few different ways to deal with your issue, and each will teach you something very important about the framework. The first two I'll mention are not suggested but you should know what is possible to do, in general, even though in this specific scenario they are.... bad, conceptually (imo)
So, first of all: You can add a property to the Model. Say, HasYear... and HasCountry. And in your view do a switch or an if and output the needed scripts and/or html.
Second: You can have each of the Controller endpoints use a different view. So, instead of return View() at the end, you return View('IndexWithElection)andreturn View('IndexWithCountry')`
And here's the actually correct way to deal with this, conceptually:
The other two endpoints you have called Index are not Index endpoints. One is Year and the other is Country. You can annotate those endpoints with, say, [Route("{year}")] and [Route("{year}/{location}")]. So now you have three different controller actions, with explicit routes, and explicit View cshtmls.
Good luck!
I am new to ember and I am building a DnD character sheet to learn and practice. Right now I am having a lot of trouble getting access to model data in a controller. After hours and hours of reading related posts it is just not clicking and I think I am misunderstanding what I am passing to the controller.
Basically what I am trying to do is grab data from a model so I can do calculations on them and then display the results of those calculations on the page.
Here is my transforms/router.js:
Router.map(function() {
this.route('characters', { path: '/'})
this.route('new', {path: 'new'});
this.route('view', {path: '/:character_id'});
});
So here I am trying to pull up the view page which has the URL of the character id. Next here is my route for the view page:
export default Route.extend({
character: function () {
return this.store.findRecord('character', id)
}
});
So this is finding the record of the character with the id I pass. My link looks like this and is coming from a component:
<h5 class="card-title">{{#link-to 'view' id}}{{name}}{{/link-to}}</h5>
Now I have my controller for the view page, which looks like this:
init(id){
let character = this.get('character')
}
When I try to log character, it is still undefined. When looking ember information in dev tools it seems the page is getting the information from the model after I refresh the page, but I just can't seem to be figure out how to grab that info in the controller itself to manipulate it.
I've been trying to figure this out for quite a while now, and its getting pretty frustrating. I currently have a work around where I do the calculations beforehand and just store all the calculated results in the model as well, but while I am learning I would like to understand how this works. Thanks is advance.
Edit: As pointed out in comments below I was missing let when defining character.
Your model hook seems wrong. You're using id but never define it. Probably what you want is more like this:
character(params) {
return this.store.findRecord('character', params.character_id);
}
Next your init hook makes no sense:
init(id){
let character = this.get('character')
}
First there is no id passed to the init hook. Second you're missing this._super(...arguments) which should always be called when you override init.
Last is that your controller is first created and later populated with the model. Also the model is populated as model property, not character.
So you could place this in your routes template and it will work:
This is character {{model.id}}
Or if you want to change something before you pass it to the template you should use a computed property in your controller:
foo: computed('model.id', function() {
return this.get('model.id') + ' is the id of the character';
}),
However for this code to run you need to use. The easiest way to use it is to put it into your template:
{{foo}}
I'm building an Ember app with Parse as my backend. Parse out of the box does something odd with relationships – they only work one way. :/ I've been down the road of trying to manually make relationships work both ways by saving both models in Ember, but that seems messy and can mean that data gets out of sync quite easily. Yuck. So now I'm looking for alternatives.
First, let me explain in a bit more detail.
Let's say I have a post and comments. Parse will store the relationship from comments -> post, but not the other way around. Or, I can manage a list of comments in a 'Pointer array', but not the one-to-one relationship from comments -> post.
I see two options:
Modify the way the API works using their Cloud Code platform. This might be the easier route, but I'd rather address this in Ember so I can open source my adapter/serializer for others who run into this issue.
Figure out a way to make multiple requests in my adapter. I imagine it would work something like this:
a. this.store.findRecord('post', id) is called in the app code.
b. Ember adapter sees that post has a hasMany relationship based on what's defined in the model.
c. The adapter generates a url that looks for comments where the post's id matches the one I'm trying to find (pretty easy to do with Parse). Yes, this means multiple requests, but only 1 more (per relationship). It would be similar to what Ember does when there is a links attribute in the Payload, but we don't have that since Parse doesn't recognize the hasMany relationship`.
Looking for 2 things. First, any thoughts on if I'm thinking about this correctly, or suggestions on other options. Second, and probably most important, some ideas on what hook I can latch onto in my adapter to fire off a second request. My initial thought was something like this:
findRecord: function(store, type, id, snapshot) {
snapshot.eachRelationship(relationship => {
// Do something to build a url for each hasMany relationship
});
return Ember.RSVP.promise(...);
}
Any help with this is much appreciated.
So, basically you're trying to override the default findRecord behaviour of ember-data.
Your current approach sounds about right. You can create an adapter and provide custom definition to methods such as findRecord, findAll, query and so on.
A typical code example can be :
findRecord(store, type, id, snapshot) {
let data = {};
const traditional = true;
return new Ember.RSVP.hash({
news: Ember.$.ajax({ url: wpUrl + 'post', dataType: "jsonp", type: 'GET' }),
events: Ember.$.ajax({ url: wpUrl + 'comments', dataType: "jsonp", type: 'GET' })
});
});
The code snippet is completely vague, but i hope you got my point..
I know there are many ways to create both static and dynamic page titles with angular, but the best way I've found so far is angular-ui-router-title.
Briefly, it works like this:
$stateProvider.state('a', {
...
resolve: {
$title: function () { return 'a title'; }
}
...
}
and in the view:
<title ng-bind="$title"></title>
Now I'd like to get localisation working dynamically too, with angular-translate. When the user changes language in my application, an event is fired on the root scope and I use $translate.use() to change the language in use.
I figured I'd just be able to do this:
<title ng-bind-template="{{$title | translate}}"></title>
where the resolved title is now a translate key, and it would work, but it doesn't. Is there any other way I can get this translation to happen at runtime?
I am confused by Backbone's built-in REST capability. I was under the impression that Backbone models, upon a model.save(), would automatically append the value identified by idAttribute to the end of the urlRoot. But I see a lot of examples online, like the one below, where the application imperatively appends the the id to the url. Why is that? Which one is better?
An example using RequireJS:
define(function(require) {
var Backbone = require('Backbone');
return Backbone.Model.extend({
urlRoot: 'http://rest-service.guides.spring.io/greeting',
url: function() {
return this.urlRoot + '?name=' + this.id;
}
});
});
Backbone assumes that you were following some common REST practices while you were designing your REST API.
For instance, an API which updates a user should be exposed as:
PUT /users/:id
rather than
PUT /users?id=:id
Of course there are some edge cases when you must rewrite the default URL function but, in general, leaving it as is means you were following the best practices while designing your REST API.
One case I can think of when rewrites are necessary is when a sub-resource is identified by multiple IDs:
PUT /apps/:appId/schedules/:scheduleId
then the url function will be:
url: function () {
return this.urlRoot + '/' + this.appId + '/schedules/' + this.id
}
When applying REST, the id is typically the unique identifier of an item that is contained by a collection (expressed in plural).
The url /greeting/id doesn't seem to make a lot of sense to me.
Parameters sent via the query string (behind the question mark) serve as filters to the collection that is currently queried.