App.Farm = DS.Model.extend
location: DS.attr 'string'
App.Field = DS.Model.extend
location: DS.attr 'string'
farm: DS.belongsTo 'farm', async: true
markerLocation: Ember.computed.any('location', 'farm.location')
Somewhere else:
location = field.get('markerLocation')
If the field's location value is empty, will location equal the farm's location value? If not, what's the way to get this done?
Basically, the question here is this:
If I want to grab the markerLocation value, and I'm not using bindings, how do I do it so it waits for the async value if the sync one is not set? Even inside an observer I am having trouble doing this.
Maybe something like this:
markerLocation: ( ->
promise = new Ember.RSVP.Promise (resolve) =>
location = #get('customLocation')
if location
resolve(location)
else
#get('farm').then (farm) ->
resolve(farm.get('location'))
).property('location', 'farm.location')
And then get it by doing something like this:
location = field.get('markerLocation').then (location) ->
# do something with location
Trying to make it work this way now, and there's some promise there (no pun).
Question: Yes
Question: Like you did it or like on this jsbin: http://emberjs.jsbin.com/pukewo/1/edit
Play aroung with line 11 and you'll see the results.
Related
I ran into some strange behavior trying to build computed properties into my Ember Data model. Here's what my .coffee file looks like:
LineItemModel = DS.Model.extend
quantity: attr 'number'
length: attr 'number'
product: DS.belongsTo 'product', async: true
priceAdjust: attr 'number', defaultValue: 0
weight: (->
return this.get('product.weight') * length
).property('product')
# Get MLF price
price: (->
# Adjust if percentage is specified
adjust = (mlf, adjustment) ->
return mlf unless adjustment
mlf += Math.ceil(mlf * adjustment / 100)
prices = this.get('product').get 'prices'
level = this.get('quote').get 'level'
price = prices.filterBy 'level', level
return if price[0] then adjust( price[0], this.get('priceAdjust') ) else 0
).property('product', 'product.#each.prices', 'quote.level', 'priceAdjust')
My problem is that in my weight method this.get('product.weight') always returns undefined. I've done some testing and anything other than this.get('product.id') returns undefined. To make thing even more confusing, this works
Ember.computed.alias('product.weight')
and this from with the price method
# Get MLF price
price: (->
console.log this.get('product.weight')
...
This is the product model for reference:
ProductModel = DS.Model.extend
...
weight: attr 'number'
prices: DS.hasMany 'price', async: true
I'm not sure if I did something wrong or if this is a bug. This is a cli app v0.1.15
So as far as I can tell, the problem is related to promises as whether or not they're resolved. Here's what I came up with so far:
weight: Ember.computed.alias 'product.weight'
Computed property easily takes care of handling the promise relationship business
# Lbs / Ft
lbs: (->
return this.get('weight') * this.get('length')
).property('weight', 'length')
Now I know I have some value for weight and I can do my calculation with it. I'm still not sure why I didn't seem to need this in the price method but this seems like the "Ember" way to do things. I'd love some feedback if this can be improved.
I have two models:
App.Focusarea = DS.Model.extend
definition: DS.attr('string')
theme: DS.belongsTo('theme',
async: true
)
App.Theme = DS.Model.extend
definition: DS.attr('string')
focusareas: DS.hasMany('focusarea',
async: true
)
when creating one of each, I would like to associate the focus area to that theme
theme = store.createRecord("theme",
id: 3
definition: 'theme definition'
)
focusarea = store.createRecord("focusarea",
id: 4
definition: 'focusarea definition'
)
theme.get("focusareas").then (focusareas) ->
focusareas.pushObject focusarea
theme.save()
but when I run my tests
theme.get('focusareas').then (focusareas)->
expect(focusareas.toArray.length).to.equal(1)
it fails - the focusareas.toArray.length equals 0, in other words, the association failed.
What am I doing wrong or what am I missing? Any help will be greatly appreciated.
Update:
Figured it out, theme.get('focusareas') returns an unresolved promise, which is 0, that when resolved will return 1 eg:
focusareas = theme.get('focusareas')
focusareas.then ->
console.log focusareas.get('length') #=1
or
store.find('theme', 4).then (theme)->
theme.get('focusareas').then ->
expect(theme.get('focusareas').get('length')).to.equal(1)
in other words
store.find('theme', 4).then (theme)->
theme.get('focusareas').then ->
theme.get('focusareas').forEach (item) ->
console.log(item.get('definition')) #'focusarea definition'
item.get('theme').then ->
console.log(item.get('theme').get('definition')) #'theme definition'
I guess I should just RTFM!
During object init you need to assign focusareas to some sort of collection that the getters and setters can work with . An ArrayProxy would work nicely in this case. How about even one you can sort items automagically with?
theme = store.createRecord("theme",
id: 3
definition: 'theme definition',
focusareas: Ember.ArrayProxy.createWithMixins Ember.SortableMixin,
content: [],
sortProperties: ['id'], // Set this to any object property
sortAscending: true
)
Hope that helped!
When I hit '/' of my app, I am getting the stack trace below
Error while loading route: TypeError: Cannot read property 'forEach' of undefined
at Function.Model.reopenClass.eachAttribute (http://localhost:3000/assets/ember-data.js?body=1:4870:32)
at JSONSerializer.extend.normalizeAttributes (http://localhost:3000/assets/ember-data.js?body=1:2906:16)
at JSONSerializer.extend.normalize (http://localhost:3000/assets/ember-data.js?body=1:2827:14)
at superWrapper (http://localhost:3000/assets/ember.js?body=1:1293:16)
at superFunction [as _super] (http://localhost:3000/assets/ember.js?body=1:7724:16)
at RESTSerializer.extend.normalize (http://localhost:3000/assets/ember-data.js?body=1:378:21)
at superWrapper [as normalize] (http://localhost:3000/assets/ember.js?body=1:1293:16)
at null.<anonymous> (http://localhost:3000/assets/ember-data.js?body=1:3179:35)
at Array.map (native)
at JSONSerializer.extend.extractArray (http://localhost:3000/assets/ember-data.js?body=1:3178:37)
Relevant Code is Here (in Coffeescript):
Plnnr.ApplicationRoute = Ember.Route.extend(
model: ->
#store.find('stage')
)
Plnnr.Stage = DS.Model.extend(
tasks: DS.hasMany("task")
name: DS.attr("string")
description: DS.attr("string")
position: DS.attr("number")
)
Plnnr.ApplicationAdapter = DS.ActiveModelAdapter.extend(
namespace: 'v1'
)
The API is setup with Rails Serializer and setting a breakpoint shows that the Adapter is successfully retrieving the data.
I also set a breakpoint in Ember-data.js, at the origin of where the failure starts (when .normalize is called in the code below):
var normalizedArray = map.call(payload[prop], function(hash) {
return typeSerializer.normalize(type, hash, prop);
}, this);
At that time, type = DS.Model and hash = the serialized API payload.
I'm new to Ember and arent familiar with how to interpret the documentation. Does anyone know what could be wrong, and have any suggestions on how I can trace the problem?
Thanks!
It turns out the problem was because I was using Coffeescript with Ember.
I declared my ember Task class like so:
class Plnnr.Task extends DS.Model
instead of
Plnnr.Task = DS.Model.extend
These two are NOT equivalent. While Stage correctly used the second convention, Task (which belongs to Stage) incorrectly used the first convention. When the JSON came back with both Stage and Task objects, the serializer threw up the trace above, because it couldn't handle Plnnr.Task parsing correctly
This question is similar to my previous one: how to communicate with Rails in Ember but some things have changed. Now I know that a kudo is modelled in the server and has the following fields:
value
comment
It belongs to a user. The current implementation is as below:
<a href="#" class="btn btn-primary" {{action "addKudo" user}}>add</a>
Sks.ApplicationRoute = Ember.Route.extend
events:
addKudo: (user) ->
console.log event
self = #
token = $('meta[name="csrf-token"]').attr 'content'
currentUserCon = #controllerFor 'currentUser'
kudosLeft = currentUserCon.get 'kudosLeft'
showFlash = (type, message) ->
$("#flash")
.addClass(type)
.empty()
.append(message)
.show()
.fadeIn()
.delay(2000)
.fadeOut 'slow'
$.post("/kudos", user_id: user.get("id"), authenticity_token: token)
.done((data, status) =>
if kudosLeft > 0
currentUserCon.decrementKudos 1
showFlash 'alert-success', 'You\'ve added a kudo!'
else
showFlash 'alert-error', 'There\'re no kudos left!'
)
.fail (data, status) ->
showFlash 'alert-error', 'Oops! An error occured!'
But now, I'd like to make use of Ember-Data and send a POST request to /kudos (and I'd like to get rid of the jQuery ajax part). Please have a look:
Having the following model:
Sks.Kudo = DS.Model.extend
user: DS.belongsTo 'Sks.User'
value: DS.attr 'number'
comment: DS.attr 'string'
how can I achieve this? What are the neccessary steps to do this? Could anyone shed some light?
The next question is: where select and text area value should point to (bindings)?
I'm writing an extension to Ember.TextField to change the class of the text field if the parent object is invalid. I'm using the ember-validations library to do the validations on my ember-data object. I have this working right now, but I'd love to be able to do it without passing in the validationMethod variable. How can I get just the text string passed in to valueBinding from within the view?
address.js.coffee
App.Address = DS.Model.extend(Ember.Validations,
street_1: DS.attr('string')
street_2: DS.attr('string')
...
validations:
street_1:
presence: true
)
validated_text_field.js.coffee
Ember.ValidatedTextField = Ember.TextField.extend(
validationMethod: null
classNameBindings: 'error'
focusOut: ->
object = #get('controller').get('content')
object.validateProperty(#get('validationMethod'))
error: (->
object = #get('controller').get('content')
if object.get("isValid")
return false
else
error_keys = object.get("validationErrors." + #get('validationMethod') + ".keys")
if error_keys
return error_keys.length > 0
else
return false
).property('controller.content.isValid')
)
edit.handlebars
{{view Ember.ValidatedTextField validationMethod="street_1" valueBinding="street_1" id="street_1" placeholder="Street 1" required="true"}}
...
Although I can't suggest it because it's internal code, you can get the strings from the bindings themselves.
validationMethod = #get("valueBinding._from").split(".").get("lastObject")
Check out how we handle the label for Ember-Bootstrap for another example: https://github.com/emberjs-addons/ember-bootstrap/blob/master/packages/ember-bootstrap/lib/forms/field.js