emberjs : dynamic url - ember.js

I'm trying to do a custom url.
More specifically, what I've already done is:
mypage.com/details/1
What I'd like to do is:
mypage.com/details/john-doe
Of course, I want exactly the same behavior as now, with the sole difference being a custom string instead of id
Here's my DS model
App.Images.FIXTURES = [
{
id:1,
name: "John",
lastName: "Doe",
age: 25,
customUrl: "john-doe"
},
{
id:2,
name: "John",
lastName: "Doe",
age: 31,
customUrl: "john-doe1"
}];
*Note the customUrl attribute which is unique.
My routes:
App.Router.map(function(){
this.resource("details", {path: 'details/:image_id'});
});
And the according html:
{{#linkTo 'details' this}}The details{{/linkTo}}
What are my options? Putting "this" in html automatically grabs the ID. I tried this.customUrl with no luck (I get the "undefined" value in the url).
Thank you for your help!

you can override serialize in router to return customUrl instead of id
App.DetailsRoute = Ember.Route.extend({
// ....
serialize: function(model) {
return { details_id: model.get('customUrl') };
}
});

Related

Accessing model properties in Controller - Ember

So, I'm trying to access my model properties in controller.
Controller:
dashobards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
];
In route I have model named dashboards
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
}).then((hash) => {
return Ember.RSVP.hash({
dashboards: hash.dashboards
});
}, self);
I wanna have result in controller like this:
dashboards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
{ id: 17, name: 'test1' },
{ id: 20, name: 'test20' },
];
In controller I am trying to access this model like this:
this.dashborads = this.get(model.dashobards)
And it's not working, is there any other way of doing that?
Another update How to access complex object which we get it from server in ember data model attibute,
Created twiddle to demonstrate
define attribute with DS.attr(),
export default Model.extend({
permissions:DS.attr()
});
route file,
model(){
return this.store.findAll('dashboard');
}
Your server response should be like,
data: [{
type: 'dashboard',
id: 1,
attributes: {
permissions: {'name':'role1','desc':'description'}
}
}]
hbs file,
{{#each model as |row| }}
Name: {{row.permissions.name}} <br/>
Desc: {{row.permissions.desc}} <br />
{{/each}}
Update:
Still I am not sure about the requirement, Your twiddle should be minimalized working twiddle for better understanding..anyway I will provide my observation,
1.
model(params) {
this.set('id', params.userID);
const self = this;
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
}).then((hash) => {
return Ember.RSVP.hash({
user: hash.user,
dashboards: hash.dashboards
});
}, self);
}
The above code can be simply written like
model(params) {
this.set('id', params.userID);
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
});
}
Its good to always initialize array properties inside init method. refer https://guides.emberjs.com/v2.13.0/object-model/classes-and-instances/
For removing entry from array,
this.dashboard.pushObject({ 'identifier': '', 'role': '' }); try this this.get('dashboard').pushObject({ 'identifier': '', 'role': '' });.
if possible instead of plain object you can use Ember.Object like
this.get('dashboard').pushObject(Ember.Object.create({ 'identifier': '', 'role': '' }));
For removing entry.
removeDashboard(i) {
let dashboard = Ember.get(this, 'dashboard');
Ember.set(this, 'dashboard', dashboard.removeObject(dashboard[i]));
}
The above code can be written like, since i is an index
removeDashboard(i) {
this.get('dashboard').removeAt(i)
}
Just do return this.store.findAll('dashboard'); in route model hook, and dont override setupController hook, then in hbs you should be able to access model that will represent RecordArray. you can have a look at this answer for how to work with this.

Ember-data return a single json object with store.find

Is this possible? I know I can do:
this.store.find('model', 1)
but that's not what I want. I would like to retrieve the json in this format: (working if I retrieve it in the route like this ):
App.OptionsRoute = Ember.Route.extend({
model: function () {
return {
"options":{
"id": "1",
"headline": "A Headline",
"results": [
{
"id": "1",
"title": 'Option 1',
},
{
"id": "2",
"title": "Option 2"
}
]
}
};
}
});
options model:
App.Options = DS.Model.extend({
headline: DS.attr(),
results: DS.attr()
});
options.hbs
<h5>{{options.headline}}</h5>
{{#each item in options.results}}
<h5>{{item.title}}</h5>
{{/each}}
I am using the RESTAdapter. And that is the only model that will be retrieved on that route. I would like to be able to use ember-data, but store.find expects an array.
You're missing a point here. First of all you're using bad format for your response. You need custom serializer. You can also use a bit more dirty workaround like this(but it works). Route:
App.OptionsRoute = Ember.Route.extend({
model: function() {
that = this;
return new Promise(function (resolve, reject) {
url = that.store.adapterFor('option').buildURL('option');
Ember.$.getJSON(url).then(function (json) {
body = json.options;
correct = {
options: [
body
]
};
that.store.pushPayload('option', correct);
resolve(that.store.all('option').get('firstObject'));
});
});
}
});
Template:
<h5>{{model.headline}}</h5>
{{#each item in model.results}}
<h5>{{item.title}}</h5>
{{/each}}
Application outputs:
A Headline
Option 1
Option 2
Working demo - please notice that I'm using $.mockjax to recreate your response from server, but it matches format you provided.

confused about asyc loading from direct url emberjs

Below are my fixture adapters.
My understanding is model hook is not called on transitionTo or link-to when a model is provided.
But when same route is reached direactly by sharing or copy pasting url model hook is called.
Now when I transition by passing a model I see the city 1 and city 2 properly.
But when I copy past url the cities are not displayed. I tried .then and then get cities still I cannot see it. I have commented that line. I know I am doing something stupid. I did googled but couldnt figure out.
here is my jsbin: BIN BIN BIN
Though this is similar to THiS question. the ans is use modelFor and then findBy. but modelFor gives model for the parent route. but in my case since its not nested routes. this.modelFor('countries') gives undefines and hence i cannot apply findBy on it.
model: function(params) {
this.store.find('country', params.countryCode).then(function(country) {
console.log(country);
//country.get('cities');
});
}
Q.Country.FIXTURES = [{
id: 1,
countryCode: "CO",
countryName: "Country",
cities: [1, 2]
}];
Q.City.FIXTURES = [{
id: 1,
cityName: "city 1",
country: 1
}, {
id: 2,
cityName: "city 2",
country: 1
}];
Q.CountryRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('country', params.countryCode);
},
serialize: function(country) {
return {
country_id: country.get("countryCode")
};
},
afterModel: function(model) {
console.log("after model was called");
//this.transitionTo('cities',model);
}
});
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_id'
});
});
You have to return something from your model() hook. No need to use then() because in the model hook Ember will automatically wait for the promise to resolve.
model: function(params) {
return this.store.find('country', params.country_id);
}
If you want to use a slug, something like this could work:
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_code'
});
});
model: function(params) {
return this.store.findQuery('country', { code: params.country_code });
}

Ember route getting empty model after successful promise

I've built a RESTAdapter to work with couchdb, and am testing it to make sure it works, and so far things seem fine, but my test route seems to be having other problems.
Sorry this is so long, I probably ought to set up a fiddle for it... I've just never done that before, but will look into it right now....
I've built the following (relevant) things:
App.Thing = DS.Model.extend({
rev: DS.attr(),
price: DS.attr()
});
App.Things<Index>Route = Ember.Route.extend({
model: function () {
return this.get('store').findAll('thing');
}
});
(I've tried ThingsRoute with and without the Index without any change)
In App.Router.map:
this.resource('things', function() {
this.route('thing', { path: ':thing_id'})
});
In App.ApplicationAdapter = DS.RESTAdapter.extend:
buildURL: function(type, id) {
id = id || '_all_docs?include_docs=true';
return this._super(type, id);
}
In App.ApplicationSerializer = DS.RESTSerializer.extend:
extractArray: function(store, type, payload, id, requestType) {
root = type.typeKey;
root = Ember.String.pluralize(root);
newJSON = {};
newJSON[root] = payload.rows.map(function(row) {
return row.doc;
});
payload = newJSON;
console.log(payload);
return this._super(store, type, payload, id, requestType);
},
normalize: function(type, hash, property) {
var json = { id: hash._id, rev: hash._rev};
delete hash._id;
delete hash._rev;
for (var prop in hash) {
json[prop] = hash[prop];
}
console.log(json);
return this._super(type, json, property);
}
And this template:
<script type="text/x-handlebars" data-template-name="things/index">
{{#each thing in things}}
{{thing.rev}}
{{thing.price}}
{{else}}
Empty.
{{/each}}
</script>
The console.logs in extractArray and normalize both show the following perfectly formatted and correct json:
Object {things: Array[3]}
Object {id: "8117701d38cf9a1112ce8ed38000064d", rev: "1-14918623fedb103cf035ff2489e0a6a1", price: 1}
Object {id: "8117701d38cf9a1112ce8ed3800006e5", rev: "1-09b1e6aa1fb391e11c90bca86daccb7a", price: 5}
Object {id: "8117701d38cf9a1112ce8ed38000144e", rev: "1-2a682bf7ce58829ad2054bb8f5fbe869", price: 4}
but when the template is rendered it simply shows Empty, and when I replace the model hook in the ThingsRoute to this:
return {things: [{id: 1, rev: 234, price: 4}, {id: 2, rev: 235, price: 3}]};
it works exactly as expected. AND when I define afterModel:
afterModel: function(things, transition) {
console.log(things);
console.log(transition);
}
It logs this:
Class {type: function, store: Class, isLoaded: true, isUpdating: false, toString: function…}
Transition {router: Router, promise: Promise, data: Object, resolvedModels: Object, providedModels: Object…}
that Class object has this:
content: Array[3]
0: Class
1: Class
2: Class
and each of THOSE Classes has an id field corresponding to my objects.
What's happening? Why isn't my route getting that model even after the Adapter seems to do it's job perfectly?
I think that your problem is because the things variable in your template, doesn't exist, try to update to model
<script type="text/x-handlebars" data-template-name="things/index">
{{#each thing in model}}
{{thing.rev}}
{{thing.price}}
{{else}}
Empty.
{{/each}}
</script>
Or if you want that variable you can create a alias in your controller:
App.ThingsIndexController = Ember.ArrayController.extend({
things: Ember.computed.alias('model')
});
You should be using find instead of findAll

ember data one-to-one association returns undefined

I have these two resources:
App.Users = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
email: DS.attr('string'),
userprofile: DS.belongsTo('App.Userprofiles', {embedded:true}),
fullName: function() {
return this.get('first_name') + ' ' + this.get('last_name');
}.property('first_name', 'last_name'),
didLoad: function() {
console.log('Developer model loaded', this);
}
});
App.Userprofiles = DS.Model.extend({
company: DS.attr('string'),
user: DS.belongsTo('App.Developers'),
didLoad: function() {
console.log('Developer Profile model loaded', this);
}
})
These are my view and controller:
App.UserInfoController = Ember.ObjectController.extend({
content: App.store.find(App.Users, 1),
}).create();
App.UserInfoView = Ember.View.extend({
controller: App.UserInfoController,
contentBinding: 'controller.content'
});
This a sample response for a user from my API
{
"email": "foo#gmail.com",
"first_name": "George",
"id": "1",
"last_name": "Eracleous",
"resource_uri": "/api/v1/users/1/",
"userprofile": {
"company": "bar",
"id": "1",
"resource_uri": "/api/v1/userprofiles/1/",
"user": "/api/v1/users/1/"
}
}
The user object is loaded correctly but when I try to do get("userprofile") I get null. Does anybody know what I am doing wrong?
In order to load embedded related objects you have to configure the serializer used by the adapter, by calling its 'map' function. The only way I know to do this is by subclassing the serializer and add an 'init' function to it, where you make the necessary calls to map. For every embedded relationship of every model class you will have to do a call to 'map'. This applies to to-one and to-many relationships. Make sure to configure your adapter to use this serializer.
For an example see my answer to a previous question.
You can also check out this example online.
As mentioned in the comment, instead of subclassing the serializer and calling its map() function in the initialiser you can directly call map() on the adapter class. As an example, here is an excerpt of my own code doing this.
WO.RESTAdapter.map(App.Category, {
resourceTypes: {
embedded: 'load'
}
});
WO.RESTAdapter.map(App.Resource, {
resourceType: {
embedded: 'load'
}
});
WO.RESTAdapter.map(App.Reservation, {
resource: {
embedded: 'load'
},
user: {
embedded: 'load'
}
});
App.serializer = App.WOSerializer.create();
App.store = DS.Store.create({
revision: 10,
adapter: WO.RESTAdapter.create({
namespace: "cgi-bin/WebObjects/Reserve.woa/ra",
serializer: App.serializer
}),
serializer: App.serializer,
adapterForType: function(type){
return this.get('adapter');
}
});