I'm trying to implement geocoding using Google maps JSON API.
I created a model for the location, and and ObjectController,
the model has the async geocoding logic.
I want the controller to observe changes to the model to reflect the most update data.
I'm trying both with binding and observe and both doesn't work:
App.Location = Ember.Object.extend({
formatted_address: null,
location: {
lat: null,
lng: null
}
})
App.Location.reopenClass({
geocoded: Ember.Object.create(),
geocode: function(address) {
$.ajax({
url: 'http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=' + address,
dataType: 'json',
context: this,
success: function(response) {
App.Location.geocoded.set('response', response);
}
});
return this.geocoded;
}
});
App.LocationController = Ember.ObjectController.extend({
contentBinding: 'App.Location.geocoded',
testProperty: function() {
console.log('test');
}.property('content'),
testObserve: function() {
console.log('test');
}.observes('App.Location.geocoded')
});
EDIT: changing to observes('App.Location.geocoded.response')fix the issue. can it also work with binding? is there a better way to do this ?
Here when you write contentBinding: 'App.Location.geocoded', the content is updated when App.Location.geocoded change. But in the success handler, you don't change the geocoded object, but just update its response property.
So, if you want to keep the binding with the geocoded object, you can try to do
App.Location.set('geocoded', Ember.Object.create({response: response}));
in the ajax success handler.
Related
When I inspect Data in ember inspector I see multiple record one with id(int) and one without id(null).
Using ember 2.9.1
save: function(){
var title = this.get('title');
this.store.createRecord('post',{ title:title }).save();
this.setProperties({ title:''});
this.transitionToRoute('posts');
}
Node Backend
router.post('/', function(req, res, next) {
post.create({
title: req.body.data.attributes.title
}).then(function () {
res.set('Content-Type','application/vnd.api+json');
return res.send({
"msg": "post created successfully",
data:null
});
});
});
I'm surprised to see a post in the data store with an id from this operation. You should be returning (assuming a RestAdapter)
router.post('/', function(req, res, next) {
post.create({
title: req.body.data.attributes.title
}).then(function (post) {
res.set('Content-Type','application/vnd.api+json');
return res.status(201).json(post);
});
});
This will return the created post record that contains the id, which in turn will update the store appropriately.
I suspect the null id is from the current save, and the record with an id is from a previous save?
return res.status(201).send({
data:{
type:'post',
attributes:{
id:post.id,
name:post.title
}
}
});
The following code works properly.This is a known issue in ember data.
I have a component which adds some functionality to a <select> tag. I want to initialise some javascript after the <select> has fully rendered including all <option> tags. The data used to populate the <option> tags is an array of objects provided from an ajax request.
I'm using ember-data and finding this works when the data is provided from the store, meaning it is an instance of DS.RecordArray which has helpful properties like isLoaded. However, when the data is provided from a jQuery ajax call and is just plain JSON, it appears as though the component tries to render everything before the promise returned by jQuery is fulfilled.
I feel the issue is with how I'm handling promises as the issue seems to be related to things initialising before they should (ie promises have resolved properly). I tried wrapping the ajax call in an RSVP.Promise object but not luck, (I'm using Ember-CLI). Below is a simplified version of what I have so far. Any help would be appreciated.
// My route
export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller, model);
var hash = Ember.RSVP.hash({
// myOptions: $.getJSON('/api/options')
myOptions: new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax({
url: '/api/options',
type: 'get',
dataType: 'json',
success: function(result) {
Ember.run(null, resolve, result);
},
error: function(result) {
Ember.run(null, reject, result);
}
});
})
});
return hash.then(function(result) {
controller.set('optionsForSelect', result.myOptions);
});
}
});
// My component
export default Ember.Select.extend({
didInsertElement: function() {
// Is called before ajax request has finished
this.$().mySelectPlugin({
});
}
});
// Handlebars template
{{my-select-plugin content=optionsForSelect optionValuPath="content.id" optionLabelPath="content.name"}}
To me, this seems like something that should be handled in the controller, not the route. Here's what I did for a similar situation.
App.LessonsShowIndexController = Ember.ObjectController.extend({
author: function() {
return this.get('model').get('author');
}.property('author'),
fullName: function() {
return this.get('author').get('firstName') + ' ' + this.get('author').get('lastName');
}.property('author.isFulfilled'),
});
Using the isFulfilled property allows the controller to wait for the promise to resolve before using the data. In your case, you could have a property that returns a promise and another that waits for it to be fulfilled.
I'm trying to use ember-data with jsonp by overridding DS.RESTAdapter's findAll (based on the answer to this question).
App.ApplicationStore = DS.Store.extend({});
App.Event = DS.Model.extend({
name: DS.attr('string')
});
App.EventAdapter = DS.RESTAdapter.extend({
findAll: function() {
var events = [];
$.ajax({
url: '...',
dataType: 'jsonp',
success: function(response) {
response.results.forEach(function(event) {
events.addObject(App.ApplicationStore.createRecord('event', event));
}, this);
}
});
return events;
}
});
App.EventsRoute = Ember.Route.extend({
model: function() {
return this.store.find('event');
}
});
I first tried using events.addObject(App.Event.create(event)), but ember returned an error: "You should not call create on a model. Instead, call store.createRecord with the attributes you would like to set".
The issue is, App.ApplicationStore.createRecord is undefined, so I'm stuck without a way to instantiate Events. Anyone know what's going on? If there's a completely different approach to getting jsonp to work with ember-data, that's fine too.
This parsing of the response seems more like a job for the RESTSerializer than the RESTAdapter(though you will still need the adapter if you need to set the dataType/url)
Not 100% sure, but it looks like your reponse is an array that doesn't have the correct key
as stated in the jsonapi.org documenation?
If this is the case, you'd want to create a serializer for events like this
App.EventsSerializer = DS.RESTSerializer.extend({
extractFindAll: function(store, type, rawPayload, id, requestType) {
this._super(store, type, { 'events': rawPayload }, id, requestType);
}
});
The above serializer will reformat the response to be ember-data readable(as per the above documentation), and ember-data will take care of the rest
DS.RESTSerializer documentation
As an aside, the current store is passed as the first parameter to DS.RESTAdapter.findAll, so you should access the store through that parameter
<\EDIT>
including DS.RESTAdapter.findall source
kaungst's answer was really helpful, but ember was still throwing an error. It led me to a solution that works, though:
App.EventSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload) {
return {'events': payload};
}
});
App.EventAdapter = DS.RESTAdapter.extend({
findAll: function(store) {
var events = [];
$.ajax({
url: '...',
dataType: 'jsonp',
success: function(response) {
response.results.forEach(function(event) {
events.addObject(store.createRecord('event', event));
}, this);
}
});
return events;
}
});
I overrode DS.RESTSerializer's normalizePayload instead of extractFindAll, which fixed the subsequent error I was getting. Additionally, I defined App.EventSerializer (singular) instead of App.EventsSerializer.
Ember Data has a reload method; I'm using a more basic approach (using Ember objects) to serve up my models, though.
I'd like a user to be able to reload the model for the current route via an action when e.g. a button is clicked. Is this possible without Ember Data?
Here is a really simple example of how I do it w/ your approach (notice the "clear").
You could invoke clear+find from your route / controller / whatever. I've also added a "beforeSend" to the $.ajax in some of my bigger apps (this will invoke the clear for you before the xhr is resolved)
App.Person.reopenClass({
people: Ember.A([]),
clear: function() {
this.people = Ember.A([]);
},
add: function(hash) {
var person = App.Person.create(hash);
this.people.pushObject(person);
},
remove: function(person) {
this.people.removeObject(person);
},
find: function() {
var self = this;
$.getJSON('/api/people', function(response) {
response.forEach(function(hash) {
var person = App.Person.create(hash);
Ember.run(self.people, self.people.pushObject, person);
});
}, this);
return this.people;
}
});
The issue was the async nature of the AJAX call I was making.
This didn't work:
this.set('model', App.MyObject.findAll(value));
I needed to allow the AJAX call to return the response, then populate the model:
var that = this;
App.MyObject.findAll(value).then(function(response) {
that.set('model', response);
});
Sorry to ask such a simple question but I'm looking at migrating from jQuery to Ember and am trying to figure out calling / responding json without using ember-data. One question I have is how do people suggest having class methods. Say for example I have a post object like this:
Hex.Post = Ember.Object.extend({
id: null,
body: null
});
Would a reasonable findById look like this?
$(document).ready(function(){
Hex.Post.findById=function(id){
console.log("you are here");
$.getJSON("/arc/v1/api/post/" + id, function(data){
var post = Hex.Post.create();
post.set('id', data.id);
post.set('body',data.body);
return post;
});
};
});
Or is this just wrong for creating a findById class method?
When I run this from the chrome console, it comes back as undefined even though the JSON call works fine in a brower. What am I doing wrong?
thx
FROM CHROME CONSOLE:
You'd want to define it on the class, and return the ajax call, which is then a promise
Hex.Post = Ember.Object.extend({
id: null,
body: null
});
Hex.Post.reopenClass({
findById: function(id) {
return Ember.$.getJSON("/arc/v1/api/post/" + id).then(function(data){
var post = Hex.Post.create();
post.set('id', data.id);
post.set('body',data.body);
return post;
});
}
});
Using the promise
from a model hook, Ember will resolve the promise for you, example below
Hex.PostRoute = Em.Route.extend({
model: function(param){
return Hex.Post.findById(param.id);
}
});
as the promise
Hex.Post.findById(42).then(function(record){
console.log(record);
});
or
var promise = Hex.Post.findById(42);
promise.then(function(record){
console.log(record);
});
And here's a simple example:
http://emberjs.jsbin.com/OxIDiVU/21/edit