I've got some static data that I'm initializing into the ember app like this
App.initializer
name:"Foo Data"
initialize: ->
store = DS.get("defaultStore")
obj =
[
id: 1
name: "whatever"
,
id: 2
name: "whenever"
]
type = App.Foo
store.loadMany(type,obj)
The following test seems to pass regardless of what 'whatever' is
it 'should be preloaded into store', ->
Ember.run ->
App.Foo.find(1).then((foo)->
foo.get('name').should.equal('whatever')
)
Is this a bug? What's the correct way to do this?
I guess I forget to check the obvious...
This
it 'should be preload into store', ->
Ember.run => App.Foo.find(1).get('name').should.equal('whatever')
Works fine
Related
I have the following model setup:
deliveryMethods: DS.hasMany("delivery-method", { async: true })
With this computed property:
### COMPUTED PROPERTIES ###
formattedDeliveryOptions: (->
#get('deliveryMethods').map((dm) ->
console.log dm.toJSON()
return { key: dm.get('name').underscore, value: dm.get('name') }
)
).property("deliveryMethods.#each")
And I am trying to access this property in a controller like so:
deliveryMethodsArray: (->
org = #get('controllers.application.currentOrganization')
console.log org.get('formattedDeliveryOptions')
return org.get('formattedDeliveryOptions')
).property()
But when the console.log dm.toJSON() runs, the organization property is set but nothing else. Not sure what I am doing wrong
console.log output:
Because the async is true, i believe you need to do a then() on the get promise.
org.get('formattedDeliveryOptions').then((fmtOpts) => { /* do something*/ });
Please excuse my syntax, you use a slightly different shorthand version than i do. The concept should be the same though.
I got live updates working with ember and Rails 4 with the help of Railscast #401. ActionController::Live on the backend and EventSource in my Ember code.
The event listener prepends a div with the content sent from the server, but there are 2 problems:
The template on my local browser updates automatically, causing 2 duplicate records to display. So I tried to create a temporary client ID and compare ids before prepending to the DOM. This proved to be quite glitchy, plus it doesn't seem like the ember way...
I found the ember store 'push' and 'pushPayload' methods but I couldn't get those to update my template either.
Here is the relevant code:
Method 1 Using DOM prepend -
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
unless self.controllerFor('postsNew').get('savedId') is data.id
$("#allposts").prepend $("<div class=\"post\">").text(data.content)
'savedId' is set in the Posts.new controller after a post is saved. This is hit or miss...
Method 2 Using store.push -
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
self.store.push "post",
id: data.id
content: data.content
The push method does not update the template.
Method 3 - Works SOME of the time
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
self.store.find("post", data.id).then (stuff) ->
console.log('PUSH NEW POST')
posts = self.modelFor('notebook').get('posts')
posts.addObject(stuff)
When I open up a Chrome and Firefox browser side by side and add new posts, they'll show up only about 60-70% of the time...still looking for where my error might be.
Thanks in advance for any help I can get.
Using Ember-Data, by adding the object that is sent by the server, the application is able to sync to the server and update the template. This seems clean and it works consistently...but only in Chrome, not in Firefox.
I'm adding it as an answer because it's a step forward for this question, but its not the correct answer yet. Hoping to find something soon.
Auth.NotebookIndexRoute = Ember.Route.extend(
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.onmessage = (e) ->
console.log('This shows up in Chrome but not in Firefox')
data = $.parseJSON(e.data)
self.store.find("post", data.id).then (stuff) ->
posts = self.modelFor('notebook').get('posts')
posts.addObject(stuff)
stuff.save()
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!
Apologies for my english,
I am trying to extend a View with a mixin. The code is coffeescript :
View - films_film_view.js.coffee
App.FilmsFilmView = Em.View.extend App.ModalViewMixin,
templateName: 'films/show'
Mixin - modal_view_mixin.js.coffee
App.ModalViewMixin = Em.Mixin.create
modalView: null
click: ->
#showModalView()
close: ->
#closeModalView()
closeModalView: ->
if #modalView
#modalView.close()
showModalView: ->
#closeModalView()
#modalView = #createModalView()
if #modalView
#modalView.append()
when i try to load the app, the app throws this error:
Assertion failed: Expected hash or Mixin instance, got [object Undefined]
so, what is the correct way to write this ?
Thanks in advance
Update:
Solved with :
App.ModalViewMixin = Em.Mixin.create
modalView: null
click: ->
#showModalView()
close: ->
#closeModalView()
closeModalView: ->
if #modalView
#modalView.close()
showModalView: ->
#closeModalView()
#set modalView, #createModalView()
if #modalView
#modalView.append()
App.FilmsFilmView = Em.View.extend App.ModalViewMixin,
templateName: "films/show"
Looks like when the app loads "FilmsFilmView", the mixin still is not initialized and it causes the error.
This happen because when running:
App.FilmsFilmView = Em.View.extend(App.ModalViewMixin, ...
App.ModalViewMixin is undefined, it have to be defined before of it usage.
Ensure that App.ModalViewMixin appears before than App.FilmsFilmView.
I'm trying to do a Jasmine test of ember-data (using the current master) using the DS.FixtureAdapter. I've tried dozens of variations on the below code (with and without trying to create an Application namespace). I've also gone into the ember-data source to try and see what's going on, as well as referenced the tests in ember-data itself as an example.
I've also tried variations of Person.find(1), using Ember.run blocks and Jasmine wait()'s.
Whatever I try, store.find(Person, 'test') returns a result but attempting to get one of the attributes results in null (test assertion fails). What is it I'm not seeing? Thanks for any help!
describe "a test", ->
store = null
Person = null
beforeEach ->
store = DS.Store.create
revision: 11
adapter: 'DS.FixtureAdapter'
Person = DS.Model.extend
firstName: DS.attr('string')
lastName: DS.attr('string')
age: DS.attr('number')
it "works or does it", ->
Person.FIXTURES = [{
id: 'test'
firstName: 'Kyle'
lastName: 'Stevens'
age: 30
}]
kyle = store.find(Person, 'test')
expect(Em.get(kyle, 'firstName')).toEqual('Kyle')
Whatever I try, store.find(Person, 'test') returns a result but attempting to get one of the attributes results in null (test assertion fails). What is it I'm not seeing? Thanks for any help!
This is a timing issue. When you call store.find() it runs query asynchronously and returns a model promise. That means the query is still running (or scheduled to run) when control returns to your test, resulting in a failed expectation.
This is what we love about ember, it means your app can treat kyle as if the data were present and trust that values will be updated automagically via bindings when the data becomes available.
Of course all this magic is not so great when it is preventing your test from passing. Here are some alternative approaches:
1) Register a didLoad callback
kyle = store.find(Person, 'test');
kyle.on('didLoad', function() {
console.log('should = kyle: ', Em.get(kyle, 'firstName'));
});
2) Instead of didLoad could use more blackbox testing approach and just verify that the name is set propertly within 100 ms of having called find - of course this can lead to brittle tests
Ember.run.later(this, function() {
console.log('should = kyle: ', Em.get(kyle, 'firstName'));
console.log('should = kim: ', Em.get(App.kim, 'firstName'));
}, 100);
I believe that in a jasmine test you could wrap your setup code in a runs() method and use waitsFor to verify that the value has been set as expected:
waitsFor(function() {
return Em.get(kyle, 'firstName') == 'Kyle';
}, "x to be set to 5", 100);
See this JSBIN for working (non-jasmine) example:
http://jsbin.com/apurac/4/edit
See this post for tips on async testing with jasmine: http://blog.caplin.com/2012/01/17/testing-asynchronous-javascript-with-jasmine/
Also, be sure to set Ember.testing = true for all of your tests. See this SO post for detail: Is it recommended to set Ember.testing = true for unit tests?