Cannot get Rails REST API to work with Ember.js - ruby-on-rails

I'm busy following this Ember.js tutorial, but I'm trying to implement it using the Ember 2.0 way of doing things (modules, using Ember CLI and the ember-cli-rails gem). It's rather difficult since none of the Ember guides follow these conventions.
As per the tutorial, I'm using Rails as a JSON API and it looks like everything works like it should in terms of serving the proper JSON responses. Problem is, I can't get my leads model to work.
I'm getting a TypeError: Cannot read property 'typeKey' of undefined error from ember.debug.js. I'm also getting a undefined is not a function error from ember.adapter.js
My project looks as follows:
app/store.js
import DS from 'ember-data';
export default DS.RESTAdapter.reopen({
url: 'https://localhost:3000',
namespace: 'api/1'
});
app/router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.resource('leads', { path: '/' });
});
export default Router;
app/adapters/application.js
import DS from "ember-data";
var ApplicationAdapter = DS.ActiveModelAdapter.extend({
host: 'http://localhost:3000',
namespace: 'api/v1'
});
export default ApplicationAdapter;
app/models/lead.js
import DS from 'ember-data';
export default DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
phone: DS.attr('string'),
status: DS.attr('string', { defaultValue: 'new' }),
notes: DS.attr('string'),
});
app/routes/leads.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() { return this.store.find('lead') }
});
I don't see any HTTP requests being made to Rails, so I assume that it breaks even before trying to use the API. Can anybody please point out what I'm doing wrong here?

Looks like the issue was store.js, which shouldn't be used here. Not sure why, but removing it causes everything to work as it should.
UPDATE: So, upon learning more of how Ember works, I now know the issue was not the file, but that Rails returns the wrong type of json back. The Ember REST adapter expects json keys to use an underscore, eg. first_name, while Rails returns it in camel case, eg. firstName.
To use Ember with Rails, you need to use the ActiveModel adapter. The newest version of ActiveModelSerialisers(AMS) supports JSON API.

Related

Is it possible to use Action Cable in Rails 6 on Sprockets?

I realize this is a bit of a non-specific question, but I'm not exactly sure where my issue lies, so bear with me please.
I am attempting to setup Action Cable in Rails 6.1.4.1 on Sprockets (no webpacker integration). After setting up a basic channel, i am getting no result; and also no errors or anything to debug, so I really am not sure whre my issue lies.
Here's a look at the integration:
# config/cable.yml
development:
adapter: async
# channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel
def subscribed
stream_from "notification_channel"
end
# app/javascript/channels/notification_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("NotificationChannel", {
connected() {
console.log("Connected to notification channel...");
},
disconnected() {
},
received(data) {
},
notify: function() {
return this.perform('notify');
}
});
# app/javascript/channels/consumer.js
import { createConsumer } from "#rails/actioncable"
export default createConsumer()
# app/javascript/channels/index.js
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
On server boot or page load, there is no indication in server logs of an active /cable channel, as well as no output in web console log. App.notification is undefined as well. And again, no errors client-side or server-side.
Does anyone know if this integration should be working normally with Sprockets? or is this just a configuration issue?

EmberJS getting user profile information

In my Rails API I am using JSONAPI structure which Ember expects by default.
I have a Rails route http://localhost:3000/profile which will return the currently logged in user JSON.
How do I make an arbitary request to this /profile endpoint in Emberjs so I can get my logged in user's JSON in my router's model() hook?
I tried following this guide here:
https://guides.emberjs.com/v2.10.0/models/finding-records/
And have this code:
return this.get('store').query('user', {
filter: {
email: 'jim#gmail.com'
}
}).then(function(users) {
return users.get("firstObject");
});
It is returning the incorrect user however. It also seems like it doesn't matter what the value of 'email' is, I can pass it 'mud' and it will return all users in my database.
Is there no way for me to make a simple GET request to /profile in my model() hook of my profile route in Ember?
Update
It has come to my attention that the filter thing in Ember is actually just appending a query parameter onto the end of the request URL.
So having my above filter, it would be like making a request:
GET http://localhost:3000/users?filter['email']=jim#gmail.com
Which doesn't help because my Rails doesn't know anything about filter query parameter.
I was hoping Ember will automatically find the user and do some black magic to filter the user to match email address for me, not me having to manually build extra logic in my Rails API to find a single record.
Hurrmmmmmmm...sure feels like I'm fighting against the conventions of Ember at the moment.
Update
Thanks to Lux, I finally got it working with the following approach:
Step 1 - Generate the User adapter:
ember generate adapter user
Step 2 - write the AJAX request in the queryRecord method override for User adapter
import ApplicationAdapter from './application';
import Ember from 'ember';
export default ApplicationAdapter.extend({
apiManager: Ember.inject.service(),
queryRecord: function(store, type, query) {
if(query.profile) {
return Ember.RSVP.resolve(
Ember.$.ajax({
type: "GET",
url: this.get('apiManager').requestURL('profile'),
dataType: 'json',
headers: {"Authorization": "Bearer " + localStorage.jwt}
})
);
}
}
});
Step 3 - make the model() hook request like so:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').queryRecord('user', {profile: true});
}
});
Well, query is for server side filtering. If you want it client-side use something like store.findAll('user').then(users => users.findBy('email', 'bla#bla.bla'));.
But this is not what you want. You have your server side filter. It's just under /profile. Not under /user.
However interesting is what /profile actually responds. A single-record-response or a multi-record-response. The best would probably a single-record-response since you only want to return one user. So how can we do this with ember? Well, we use store.queryRecord().
And because ember does not know anything about /profile we have to tell it ember in the user-adapter with something like this:
queryRecord: function(store, type, query) {
if(query.profile) {
return Ember.RSVP.resolve(Ember.$.getJSON('/profile'));
}
}
And then you can just return store.queryRecord('user', { profile: true })

Ember - ember.debug.js:19746 Error: Failed to create an instance of 'adapter:application'

I am building an ember app and am attempting to connect to a rails API, to read in data from a questions end point.
These are two separate applications.
My Code is as follows:
adapters/application.js
import DS from 'ember-data';
import ActiveModelAdapter from 'active-model-adapter';
export default DS.ActiveModelAdapter.extend({
host: 'http://localhost:3000/api/v2'
});
models/questions.js
import DS from 'ember-data';
export default DS.Model.extend({
text: DS.attr('string'),
title: DS.attr('string'),
debateTopicId: DS.attr('string'),
releaseAt: DS.attr('date'),
expiresAt: DS.attr('date')
});
routes/questions.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('question');
}
});
templates/questions.hbs
<h2>This is the Questions Feed</h2>
{{#each model as |question|}}
<h3>{{question.id}} - {{question.text}}</h3>
{{/each}}
The end point I am attempting to call to is:
http://localhost:3000/api/v2/questions
I get the error:
ember.debug.js:19746 Error: Failed to create an instance of 'adapter:application'. Most likely an improperly defined class or an invalid module export.
at instantiate (ember.debug.js:1477)
at lookup (ember.debug.js:1336)
at Object.lookup (ember.debug.js:1255)
at Class.lookup (ember.debug.js:34526)
at ContainerInstanceCache.instanceFor (container-instance-cache.js:62)
at ContainerInstanceCache._findInstance (container-instance-cache.js:51)
at ContainerInstanceCache.get (container-instance-cache.js:39)
at Class.retrieveManagedInstance (store.js:2105)
at Class.lookupAdapter (store.js:2111)
at Class.adapterFor (store.js:2051)
Any ideas what the issue is?

Binding to relationship property

I'm trying to bind an attribute of a DS.belongsTo relationship like this:
App.User = DS.Model.extend({
name: DS.attr('string')
});
App.Post = DS.Model.extend({
text: DS.attr('string'),
user: DS.belongsTo('App.User'),
userNameBinding: 'user.name'
});
I know this example is a little bit stupid, but the idea is here.
Unfortunately, it does not work (in model.js, at this line):
Uncaught TypeError: Cannot call method 'send' of null
I also tried to use Ember.Binding.oneWay, but it does not work either. My current workaround is pretty ugly:
App.Post = DS.Model.extend({
// code omitted
userName: function() {
return this.get('user.name');
}.property('user.name')
});
You can test it in this JSFiddle.
Ember version used:
ember-data on master
ember v1.0.0-pre.2-311-g668783a
There appears to be a bug with bindings to properties that rely on state set up in init. I have filed a bug on the Ember issue tracker.
For a less ugly solution, you can use Ember.computed.alias:
App.Post = DS.Model.extend({
text: DS.attr('string'),
user: DS.belongsTo('App.User'),
userName: Ember.computed.alias('user.name')
});
I have a working example in this JSBin.

How do I get / retrieve my REST data using ember-data?

I've been scouring for documentation on the REST adapter that is packaged with ember data, but I can't seem to find any information on how to actually have ember make the json request to the server, or how to retrieve or access the data once it has made the request using this adapter ( the documentation on the ember-data page seems to all be about rolling your own adapter, besides a small paragraph on how to specify if you need to disable bulk commits, though maybe I'm just missing something )
You have to tell your store to use the DS.RESTAdapter and this handles the communication with your server via AJAX calls, see a basic example here
You can get a basic overview how the RESTAdapter is used in the tests.
App = Ember.Application.create({});
App.store = DS.Store.create({
revision: 3,
adapter: DS.RESTAdapter.create({
ajax: function(url, type, hash) {
console.log(arguments);
}
})
});
App.Person = DS.Model.extend({
});
App.Person.createRecord({
});
// tell the store to contact REST service
App.store.commit();
​

Resources