Reputation: 105
Im creating an app for Sharepoint 2013 with EmberJS and Ember Data(latest beta). Sharepoint uses an oData/RESTfull service. I've created an adapter for the models that I use. But I can't solve the following problem:
App.User = DS.Model.extend({
Email: DS.attr('string'),
Title: DS.attr('string')
});
App.PIF = DS.Model.extend({
Title: DS.attr('string'),
GUID: DS.attr('string'),
AuthorId: DS.belongsTo('user', { async: true})
});
I have these two models and as you can see the AuthorId is a user that is loaded asynchronic. When i try to display the Title of the authorId the value isn't displayed and when i log it, it is undefined. When i just display the authorID it returns a promise. My guess is that the promise is not resolved when the template created and the data is injected.
Template script:
<script type="text/x-handlebars" id="index">
<ul>
{{#each item in model}}
{{log item.AuthorId}}
<li>{{item.Title}} Author: {{item.AuthorId.Title}}</li>
{{/each}}
</ul>
</script>
Rendered when i only display the authorId and not a property of the user.
Test1 Author: <DS.PromiseObject:ember412>
Test2 Author: <DS.PromiseObject:ember414>
Test3 Author: <DS.PromiseObject:ember416>
test 4 Author: <DS.PromiseObject:ember418>
The adapter code is:
App.UserAdapter = App.OdataAdapter.extend({
findAll: function () {
return this.ajax("../../_api/web/siteusers", "get");
},
find: function (store, type, id, record) {
return this.ajax("../../_api/web/getuserbyid("+ id +")", "get");
}
});
App.PIFAdapter = App.OdataAdapter.extend({
findAll: function () {
return this.ajax("../../_api/web/lists/GetById(guid'e6a89c0b-3b24-4c53-9978-3d3e7ae3f392')/Items", "get");
}
});
My question is, if I am correct and how to solve it.
Upvotes: 1
Views: 871
Reputation: 76
The problem is that the PromiseObject does not resolve before filling the template before it gets rendered onto the DOM.
The solution I found was to create a helper that resolved the Promise and then returned that to Handlebars.
Since your AuthorId object is actually the User object, and not just the ID value, you can refactor your models to look like this:
# ./app/scripts/models/user.js
App.User = DS.Model.extend({
email: DS.attr('string'),
title: DS.attr('string')
});
# ./app/scripts/models/pif.js
App.Pif = DS.Model.extend({
title: DS.attr('string'),
guid: DS.attr('string'),
author: DS.belongsTo('user', { async: true})
});
In keeping with naming convention, I would also encourage lowercase attribute names.
Now you can make a helper that looks like this (in CoffeeScript):
# ./app/scripts/helpers/author-name.coffee
authorName = (value) ->
# `value` is whatever you pass through from the template. In this case, it'll be the pif model object.
# This needs to be a string.
_authorId = value._data.author_id.toString()
_authorName = @get('users').findBy('id', _authorId).get('title')
return _authorName
AuthorNameHelper = Ember.Handlebars.makeBoundHelper authorName
App.register('helper:author-name', AuthorNameHelper)
Now that you have this helper registered with Handlebars (you do need to call the author-name.js (compiled) file wherever you're loading in your scripts), you can use that helper in your .hbs/.handlebars files like so:
<script type="text/x-handlebars" id="index">
<ul>
{{#each pif in pifs}}
<li>{{pif.title}} Author: {{author-name pif}}</li>
{{/each}}
</ul>
</script>
As a brief note on your Ember Data models:
For this to work, regardless of how you get the data into your store, this.store
will have to be filled with the model data when your helper calls them. That means the models need to be accessible to your controller that's responsible for the index template (ideally, then the IndexController).
To load multiple models for one controller, you would provide them in an Ember.RSVP.hash. An example of your route might look like this then (again, in CoffeeScript):
# ./app/scripts/routes/index.coffee
App.IndexRoute = Ember.Route.extend
model: ->
return Ember.RSVP.hash
users: @store.find('user')
pifs: @store.find('pif')
--
In case my solution doesn't work, you might try looking here: Rendering resolved promise value in Ember handlebars template
That solution didn't work for me because this
could not access the other models. YMMV--others found their answer to work.
Upvotes: 1