Implementing custom ajax calls when loading data in model - ember.js

In my Emberjs application I have an Employee model which I should load through a REST Get API call, where I have to authenticate the API first for a token then start loading the data, I know how to do this easily using JQuery but not sure how I can implement this in EmberJS, so I will appreciate it so much if anyone can instruct me how to do so.
Below is the JQuery code I use for authentication, extracting the employees data, as well as my EmberJS model code
Thanks
Authentication:
$.ajax
({
type: "POST",
url: "http://portal.domainname.com/auth",
dataType: 'json',
async: false,
data: JSON.stringify({
Login: "logmein#email.com",
Password : "test"
}),
success: function(data) {
console.log(data); //Here I get the token needed for further calls...
},
error: function(xhr, error){
console.debug(xhr); console.debug(error);
}
});
Calls to load employees data:
$.ajax ({
type: "GET",
url: "http://portal.domainname.com/employees",
dataType: 'json',
async: false,
beforeSend: function (xhr) {
xhr.setRequestHeader ("Token", "0000000-0000-0000-0000-00000000");
},
success: function(data) {
console.log(data);
},
error: function(xhr, error){
console.debug(xhr); console.debug(error);
} });
EmberJS Model
App.Store = DS.Store.extend({
revision: 11
});
App.Employee = DS.Model.extend({
employeeID: DS.attr('string'),
employeeName: DS.attr('string')
});
App.Store.reopen({
adapter: 'DS.RESTAdapter'
});

You can add headers to all Ember AJAX requests like this:
App.Store = DS.Store.extend({
revision: 13,
adapter: DS.RESTAdapter.extend({
ajax: function(url, type, hash) {
if (!hash) {
hash = {};
}
hash.beforeSend = function(xhr) {
xhr.setRequestHeader("Authorization", "Token " + window.sessionToken);
};
return this._super(url, type, hash);
}
})
});
I use this code in production.

A very real & viable solution is to avoid using EmberData and just use ajax the way you already know. Take a look at this tutorial from a founder of Discourse (which uses Ember without Ember Data):
http://eviltrout.com/2013/03/23/ember-without-data.html

As a hack you could use something like https://api.jquery.com/jQuery.ajaxPrefilter/ for adding the header with the token to every call. However, I think you should use a dedicated auth library for this.
Also your store has revision: 11 - that is for an old version I believe.

Try something like this:
App.ApplicationAdapter = DS.RESTAdapter.extend({
setHeaders: function() {
this.set('headers', { "Token": "0000000-0000-0000-0000-00000000" });
}.on('init');
});
I think you'll need ember-data-1.0.0-beta.x for this to work.

Related

Error evaluating App.Product.findQuery

I'm trying to get data from an API like this:
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({
host: 'http://api.my-api/v1/products(name=my-name)'
})
});
App.Product = DS.Model.extend({
name: DS.attr('string')
});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return App.Product.findQuery({show: 'sku,name', format: 'json', apiKey: 'MyApIkEy123'});
}
});
The error I get in the console is:
Error while processing route: index undefined is not a function (evaluating 'App.Product.findQuery({show: 'sku,name', format: 'json', apiKey: 'MyApIkEy123'})')
The JSON should look like this:
{
"from": 1,
"to": 10,
"total": 10,
"products": [
{
"sku": 1234567,
"name": "Great Product"
}
}
They are several problems on your post.
The first one is that you do not run App.Product.findQuery in your route but a this.store.find(yoursamequery) as App.Product extends DS.Model and DS.Model dosen't have findQuery method (thus you get undefined is not a function :))
http://emberjs.com/api/data/classes/DS.Model.html
I think that your "format" and "apiKey" are not data filter but request parameters which have to be passed to your backend api right ? If so you should create an applicationAdapter with those parameters defined as in the documentation example :
http://emberjs.com/api/data/classes/DS.RESTAdapter.html
In the model hook, try using:
return this.store.findQuery('product', {show: 'sku,name', format: 'json', apiKey: 'MyApIkEy123'});
It looks like you are trying to get your API to provide attributes that aren't in your model (i.e. sku, salePrice). Is that right? What does the response to that API call look like? If Ember Data is trying to set those attributes in your model object and not finding them, this could be the issue.

ember-simple-auth custom authenticator using ember-data

I'm creating a frontend for a C program with an embedded web interface that has a simple REST api. I'd like to use ember-simple-auth with a customer authenticator and authorizer that talks to that api endpoint.
var Authenticator = AuthenticatorBase.extend({
restore: function(data) {
...
},
authenticate: function(credentials) {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
... ??? ...
});
},
invalidate: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve) {
... ??? ...
});
},
});
In the ember-simple-auth examples, I see the custom authenticator implementation that uses Ember.$.ajax to post to server like this:
authenticate: function(credentials) {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax({
url: _this.tokenEndpoint,
type: 'POST',
data: { username: credentials.identification,
password: credentials.password },
}).then(function(response) {
Ember.run(function() {
resolve({ token: response.session.token });
});
}, function(xhr, status, error) {
var response = JSON.parse(xhr.responseText);
Ember.run(function() {
reject(response.error);
});
});
});
},
But I'd rather use ember-data for this (I think) -- new to ember and ember-data, so it's not clear. Assuming my API endpoint is /session, what would my authenticate method look like with ember-data?
On a related note: I'm using ember-cli and running ember server for development. How do I tell ember-data to point to my C-based server for the REST calls? I'm trying this, but doesn't seem to be affecting the ember-data calls - they just go to the ember server:
// app/adapters/application.js
import DS from "ember-data";
export default DS.RESTAdapter.extend({
host: 'localhost:48880',
namespace: '/'
});
When you want to use Ember Data you'd need to have a Session model or so so that when you create an instance of that a POST to /sessions would be triggered. I don't actually think that makes sense though and you don't really get any benefits from using Ember Data in that case - I'd recommend to simply go with plain Ember.$.ajax and use Ember Data for your actual model data.

Trying to use jsonp with ember-data, and unable to use Ember App's store.createRecord in custom adapter

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.

How can I fetch a single model from the server

How can I fetch a single model from the server using Ember Data?
For example, I have url for my server api localhost:8080/rest/setting, and respond is JSON object
{
"siteName": "lala",
"siteUrl": "blabla"
}
In the result I want to use this model for rendering in my template.
I'm going to assume you meant to say "...can I fetch a single model from the server without using Ember Data", in which case of course you can! Just use jQuery to get your data. For example:
App.ExampleRoute = Em.Route.extend({
model: function (params) {
return Ember.$.ajax({
type: "GET",
url: 'localhost:8080/rest/setting',
dataType: 'json'
});
}
App.Setting = DS.Model.extend({
sitename: DS.attr()
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:8080', // this isn't necessary, since it'll use the current server's host
namespace: 'rest' // this is appended to your current web server host http://server/rest
});
App.Router.map(function() {
this.resource('foo', {path:'/:foo_id'});
});
App.FooRoute = Em.Route.extend({
model: function(params){
// if param.foo_id is 1 then
// this would be http://localhost:8080/rest/setting/1
return this.get('store').find('setting', params.foo_id);
}
});
your JSON should come back in this format:
{ setting: { "siteName": "lala", "siteUrl": "blabla" } }
If you want it to be in a different format you need to use a serializer and massage the data into the format Ember is expecting: https://github.com/emberjs/data/blob/master/TRANSITION.md

Ember js how to use the BasicAdapter

I am looking for a guide that will help me understand syncing models in ember. I tried to use the RestAdapter on the latest build and I am getting an error. So I decided to use the BasicAdapter based on stabilizing ember data on the ember js site.
Here is my model:
App.Accounts = DS.Model.extend({
name:DS.attr('string')
,date:DS.attr('date')
})
Here is where I declare the sync functions for the model.
App.Accounts.sync = {
list: function() {
$.ajax({
type: 'POST',
cache: false,
url: contextPath + 'account/list',
success: function(data) {
this.load()
},
error: function(jqXHR, textStatus, errorThrown) {
},
async: false
});
}
}
I am calling the list function in a setup controller:
App.TestRoute = Ember.Route.extend({
setupController:function(){
App.Accounts.list()
}
})
The function does not execute. What is the correct way to call sync functions in an ember application? Also, I could use a blog/article on this topic.
Thanks!
Have you checked out Ember Model it gives you a basic adapter without having to use EmberData.
Erik has a tutorial at embercasts.com you may need to signup to be beta user.
var attr = Ember.attr;
App.User = Ember.Model.extend({
id: attr(),
name: attr()
});
App.User.url = "/users";
App.User.adapter = Ember.RESTAdapter.create();
var newUser = App.User.create({name: "Erik"});
newUser.save(); // POST to /users.json
var existingUser = App.User.find(1); // GET /users/1.json
existingUser.set('name', 'Kris');
existingUser.get('isDirty'); // => true
existingUser.save(); // PUT /users/1.json
Cheers