I'm starting up with ember and trying to get some data from an Api and assign it to an Emberjs model.
My api returns something like:
[
{
email: 'someemail#domain.com'
name: {
firstname: 'first name',
lastname: 'last name'
}
}
{
email: 'someemail2#domain.com'
name: {
firstname: 'another first name',
lastname: 'another last name'
}
},
{
email: 'someemail3#domain.com'
name: undefined
}
]
And my code is:
App.Store = DS.Store.extend({
revision: 12,
});
App.Store.registerAdapter('App.Users', DS.Adapter.extend({
findAll: function(store, type, id) {
$.getJSON('https://domain.com/users?callback=?', {
'auth_token': token
}).done(function(json){
store.loadMany(type, json);
});
},
}));
App.Store.registerAdapter('App.Users', DS.Adapter.extend({
findAll: function(store, type, id) {
$.getJSON('https://domain.com/users?callback=?', {
'auth_token': token
}).done(function(json){
store.loadMany(type, json);
});
},
}));
App.Router.map(function() {
this.route("users", { path: "/users" });
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.Users.find();
}
});
App.Users = DS.Model.extend({
email: DS.attr('string'),
firstname: DS.attr('string'),
didLoad: function() {
console.log('model loaded', this.toJSON());
}
});
<script type="text/x-handlebars" data-template-name="users">
{{#each model}}
{{email}} - {{firstname}}
{{/each}}
</script>
Obviously firstname is empty for every object in the array.
Does anyone know what's the proper way to do this?
Thanks
Have you tried setting up your User model like this:
App.Adapter.map('App.User', {
name: {embedded: 'always'}
});
App.User = DS.Model.extend({
email: DS.attr('string'),
name: DS.belongsTo('App.UserName')
});
App.UserName = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
Related
I'm following this tutorial on the Apollo blog (here's my forked repo), and I've been going over this for a solid day, and still can't figure out why my resolvers aren't being used, so turning to help here. As near as I can tell, I've tried it exactly as the tutorial claims.
I was able to return data from mocks, so everything up to that point was working.
Here's the simple schema.js:
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
//import mocks from './mocks';
import { resolvers } from "./resolvers";
const typeDefs = `
type Author {
id: Int
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int
title: String
text: String
views: Int
author: Author
}
type Query {
getAuthor(firstName: String, lastName: String): Author
allAuthors: [Author]
}
`;
const schema = makeExecutableSchema({ typeDefs, resolvers });
// addMockFunctionsToSchema({ schema, mocks, preserveResolvers: true});
export default schema;
And my resolvers.js file:
const resolvers = {
Query: {
getAuthor(_, args) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
allAuthors: () => {
return [{ id: 1, firstName: "Hello", lastName: "world" }];
}
},
Author: {
posts: (author) => {
return [
{ id: 1, title: 'A post', text: 'Some text', views: 2 },
{ id: 2, title: 'A different post', text: 'Different text', views: 300 }
];
}
},
Post: {
author: (post) => {
return { id: 1, firstName: 'Hello', lastName: 'World' };
}
}
};
export default resolvers;
Given that I'm returning static objects, there's no need worry about syncronicity, so any ideas on why my query:
query {
getAuthor {
firstName
}
}
is returning null?
{
"data": {
"getAuthor": null
}
}
Change:
Query: {
getAuthor(_, args) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
to:
Query: {
getAuthor(root, {_, args}) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
root is not really well documented but you have to include it when you are doing queries on base schema types. You are trying to return an Author type which is an entry point into the GraphQL API because getAuthor is a root query, so therefore you must include root.
Check out https://www.howtographql.com/graphql-js/2-a-simple-query/. Read the section called:
The query resolution process
It explains the root object a little better.
Using an older version of ember(1.7.1) and ember-data(1.0.0 beta 12) as trying to upgrade to 2.0. When loading my application I get the following warning:
WARNING: The payload for 'App.QPAC' contains these unknown keys: [abs_parent_gbid,author,comments,course,follows,has_more_comments,is_announcement,more_comments_count,resource_uri,title]. Make sure they've been defined in your model.
Here is my model code (There are other models than post but they aren't being loaded in this instance).
App.Feedstory = DS.Model.extend({
global_id: DS.attr('string'),
story_type: DS.attr('string'),
story: DS.belongsTo('qPAC', {polymorphic: true}),
});
App.QPAC = DS.Model.extend({
global_id: DS.attr('string'),
global_code: DS.attr('string'),
body: DS.attr('string'),
});
App.Post = App.QPAC.extend({
title: DS.attr('string'),
is_announcement: DS.attr('boolean'),
author: DS.belongsTo('user'),
comments: DS.hasMany('comment'),
});
Ember inspector is showing me that the all the feedstory objects of type Post are being found and it shows in the Post attributes that the correct IDs are being loaded but not the other attributes.
The warning says it's looking for the post attributes in the App.QPAC model and not App.Post. Any ideas?
Edit: Added serializers and adapters
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api/v2'
});
App.FeedstorySerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: 'global_id',
attrs: {
pin: {embedded: 'always' },
story: {embedded: 'always'},
},
});
App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: 'global_id',
attrs: {
author: {embedded: 'always' },
course: {embedded: 'always' },
pin: {embedded: 'always' },
comments: {embedded: 'always'},
follows: {embedded: 'always'},
},
normalize: function(type, hash, prop) {
if (Object.keys(hash.comments).length === 0) {
hash.comments = null;
}
if (Object.keys(hash.follows).length === 0) {
hash.follows = null;
}
return this._super(type, hash, prop);
},
});
I have been battling the level 7.4 review question with no luck:
Rating isn’t a object like Review was, so our createRating function will be a little different. We can addObject the currently selected rating to the array of ratings on our product. You’ll need to save the product to update it in the store.
I can not seem to get the value from the select list. Can anyone point me in the right direction or collaborate on this?
My handlebars code:
<script type='text/x-handlebars' data-template-name='product'>
<div class='row'>
<div class='col-sm-7'>
<h2>{{title}}</h2>
<h3 class='text-success'>${{price}}</h3>
<p class='text-muted'>{{description}}</p>
<p class='text-muted'>This Product has a {{rating}} star rating!</p>
<p>Finely crafted by {{#link-to 'contact' crafter}}{{crafter.name}}{{/link-to}}.</p>
{{render 'reviews' reviews}}
<div class='new-rating'>
<h3>Rate {{title}}</h3>
</div>
<div class='new-review'>
<h3>Review {{title}}</h3>
{{#if text}}
<p class='text-muted'>{{text}}</p>
{{/if}}
{{textarea valueBinding='text'}}
<button {{action 'createReview'}} class='btn-primary'>Review</button>
</div>
</div>
<div class='col-sm-5'>
<img {{bind-attr src='image'}} class='img-thumbnail img-rounded'/>
</div>
</div>
{{contact-details contact=crafter className='row'}}
{{view Ember.Select content=ratings value=selectedRating}}
<button {{action 'createRating'}} class='btn-primary'>Rating</button>
</script>
My js code:
var App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function() {
this.route('credits', { path: '/thanks' });
this.resource('products', function() {
this.resource('product', { path: '/:product_id' });
this.route('onsale');
this.route('deals');
});
this.resource('contacts', function() {
this.resource('contact', { path: '/:contact_id' });
});
});
App.IndexController = Ember.ArrayController.extend({
productsCount: Ember.computed.alias('length'),
logo: 'images/logo-small.png',
time: function() {
return (new Date()).toDateString();
}.property(),
onSale: function() {
return this.filterBy('isOnSale').slice(0,3);
}.property('#each.isOnSale')
});
App.ContactsIndexController = Ember.Controller.extend({
contactName: 'Anostagia',
avatar: 'images/avatar.png',
open: function() {
return ((new Date()).getDay() === 0) ? "Closed" : "Open";
}.property()
});
App.ProductsController = Ember.ArrayController.extend({
sortProperties: ['title']
});
App.ContactsController = Ember.ArrayController.extend({
sortProperties: ['name'],
contactsCount: Ember.computed.alias('length')
});
App.ReviewsController = Ember.ArrayController.extend({
sortProperties: ['reviewedAt'],
sortAscending: false
});
App.ContactProductsController = Ember.ArrayController.extend({
sortProperties: ['title']
});
App.ProductController = Ember.ObjectController.extend({
text: '',
ratings: [1,2,3,4,5],
selectedRating: 5,
actions: {
createReview: function(){
var review = this.store.createRecord('review', {
text: this.get('text'),
product: this.get('model'),
reviewedAt: new Date()
});
var controller = this;
review.save().then(function() {
controller.set('text', '');
controller.get('model.reviews').addObject(review);
});
},
createRating: function(){
var rating = this.store.createRecord('rating', {
rating: this.get('selectedRating.value'),
product: this.get('model'),
reviewedAt: new Date()
});
var controller = this;
rating.save().then(function() {
controller.get('model.rating').addObject(rating);
});
}
}
});
App.ProductsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('product');
}
});
App.ContactsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.findAll('product');
}
});
App.ProductsIndexRoute = Ember.Route.extend({
model: function(){
return this.store.findAll('product');
}
});
App.ProductsOnsaleRoute = Ember.Route.extend({
model: function(){
return this.modelFor('products').filterBy('isOnSale');
}
});
App.ProductsDealsRoute = Ember.Route.extend({
model: function(){
return this.modelFor('products').filter(function(product){
return product.get('price') < 500;
});
}
});
App.ProductDetailsComponent = Ember.Component.extend({
reviewsCount: Ember.computed.alias('product.reviews.length'),
hasReviews: function(){
return this.get('reviewsCount') > 0;
}.property('reviewsCount')
});
App.ContactDetailsComponent = Ember.Component.extend({
productsCount: Ember.computed.alias('contact.products.length'),
isProductive: function() {
return this.get('productsCount') > 3;
}.property('productsCount')
});
App.ProductView = Ember.View.extend({
isOnSale: Ember.computed.alias('controller.isOnSale'),
classNameBindings: ['isOnSale']
});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Product = DS.Model.extend({
title: DS.attr('string'),
price: DS.attr('number'),
description: DS.attr('string'),
isOnSale: DS.attr('boolean'),
image: DS.attr('string'),
reviews: DS.hasMany('review', { async: true }),
crafter: DS.belongsTo('contact', { async: true }),
ratings: DS.attr(),
rating: function(){
return this.get('ratings').reduce(function(previousValue, rating) {
return previousValue + rating;
}, 0) / this.get('ratings').length;
}.property('ratings.#each')
});
App.Product.FIXTURES = [
{ id: 1,
title: 'Flint',
price: 99,
description: 'Flint is a hard, sedimentary cryptocrystalline form of the mineral quartz, categorized as a variety of chert.',
isOnSale: true,
image: 'images/products/flint.png',
reviews: [100,101],
crafter: 200,
ratings: [2,1,3,3]
},
{
id: 2,
title: 'Kindling',
price: 249,
description: 'Easily combustible small sticks or twigs used for starting a fire.',
isOnSale: false,
image: 'images/products/kindling.png',
reviews: [],
crafter: 201,
ratings: [2,1,3,3]
},
{
id: 3,
title: 'Matches',
price: 499,
description: 'One end is coated with a material that can be ignited by frictional heat generated by striking the match against a suitable surface.',
isOnSale: true,
reviews: [],
image: 'images/products/matches.png',
crafter: 201,
ratings: [2,1,3,3]
},
{
id: 4,
title: 'Bow Drill',
price: 999,
description: 'The bow drill is an ancient tool. While it was usually used to make fire, it was also used for primitive woodworking and dentistry.',
isOnSale: false,
reviews: [],
image: 'images/products/bow-drill.png',
crafter: 200,
ratings: [1,3,3]
},
{
id: 5,
title: 'Tinder',
price: 499,
description: 'Tinder is easily combustible material used to ignite fires by rudimentary methods.',
isOnSale: true,
reviews: [],
image: 'images/products/tinder.png',
crafter: 201,
ratings: [2,1,3]
},
{
id: 6,
title: 'Birch Bark Shaving',
price: 999,
description: 'Fresh and easily combustable',
isOnSale: true,
reviews: [],
image: 'images/products/birch.png',
crafter: 201,
ratings: [2,3,5]
}
];
App.Contact = DS.Model.extend({
name: DS.attr('string'),
about: DS.attr('string'),
avatar: DS.attr('string'),
products: DS.hasMany('product', { async: true })
});
App.Contact.FIXTURES = [
{
id: 200,
name: 'Giamia',
about: 'Although Giamia came from a humble spark of lightning, he quickly grew to be a great craftsman, providing all the warming instruments needed by those close to him.',
avatar: 'images/contacts/giamia.png',
products: [1,4]
},
{
id: 201,
name: 'Anostagia',
about: 'Knowing there was a need for it, Anostagia drew on her experience and spearheaded the Flint & Flame storefront. In addition to coding the site, she also creates a few products available in the store.',
avatar: 'images/contacts/anostagia.png',
products: [2,3,5,6]
}
];
App.Review = DS.Model.extend({
text: DS.attr('string'),
reviewedAt: DS.attr('date'),
product: DS.belongsTo('product')
});
App.Review.FIXTURES = [
{
id: 100,
text: "Started a fire in no time!"
},
{
id: 101,
text: "Not the brightest flame, but warm!"
}
];
After detailed research, the answer turned out being:
createRating: function() {
var prod = this.get('model');
var ratings = this.get('model.ratings');
ratings.addObject(this.selectedRating);
prod.save();
}
In the code shown, you should be getting the rating using this.get('selectedRating') not this.get('selectedRating.value')
http://emberjs.jsbin.com/xohidixe/1/edit
My serialzer works fine, exept for underscored properties. The sctucture of the JSON from server is:
var services = {
services:[{
id:8,
name:"Codin'",
service_category:{
id:5,
iso_code:"BDT",
prop:"Ohmmmm"
}
},
{
id:7,
name:"PR",
service_category:{
id:2,
iso_code:"SFD",
prop:"Naraya"
}
}]
};
after serialisation the payload looks like this:
var services = {
services:[{
id:8,
name:"Codin'",
service_category:5
},
{
id:7,
name:"PR",
service_category:2
}],
serviceCategories:[{
id:5,
iso_code:"BDT",
prop:"Ohmmmm"
},
{
id:2,
iso_code:"SFD",
prop:"Naraya"
}
]
};
But it in the template i cant access serviceCategory's prop
The models
App.Service = DS.Model.extend({
name: DS.attr('string'),
serviceCategory: DS.belongsTo('serviceCategory')
});
App.ServiceCategory = DS.Model.extend({
iso_code: DS.attr('string'),
prop:DS.attr()
});
Here is the JsBin as usual: http://jsbin.com/OxIDiVU/565
Your json has service_category as the property name in the service.
Easy fix is:
App.Service = DS.Model.extend({
name: DS.attr('string'),
service_category: DS.belongsTo('serviceCategory')
});
and
<td>{{item.service_category.prop}} </td>
http://jsbin.com/OxIDiVU/570/edit
If I create a new movie object, the associated username from owner (App.User) isn't shown. It shows only after I reload the page. Any idea how I can achieve to show immediately the associated username after I have created a new movie?
Code so far:
App.Movie = Ember.Model.extend({
objectId: Ember.attr(),
title: Ember.attr(),
year: Ember.attr(),
owner: Ember.belongsTo('App.User', {
key: 'owner',
serializer: UserType
})
});
App.User = Ember.Model.extend({
objectId: Ember.attr(),
username: Ember.attr(),
});
App.Movie.adapter = Ember.Adapter.create({
createRecord: function(record) {
return Ember.$.ajax({
headers: {
'X-Parse-Application-Id': '',
'X-Parse-REST-API-Key': ''
},
type: 'POST',
url: 'https://api.parse.com/1/classes/Movie',
contentType: 'application/json',
data: JSON.stringify(record)
}).then(function(data) {
record.load(data.objectId, record.get('_data'));
record.didCreateRecord();
});
}
});
{{#each movie in this}}
<tr>
<td>{{movie.year}}</td>
<td>{{movie.title}}</td>
<td>{{movie.owner.username}}</td>
</tr>
{{/each}}
App.MoviesIndexController = Ember.ArrayController.extend({
rawDescription: '',
year: '',
title: '',
errors: null,
actions: {
createMovie: function () {
var rawDescription = this.get('rawDescription');
if (!rawDescription.match(/([^$]+)(\d{4})/)) {
this.set('errors', {
rawDescription: 'Oh snap! Please include the movie\'s title and year.'
});
} else if (!this.isUnique({
rawDescription: rawDescription
})) {
this.set('errors', {
rawDescription: 'Oh snap! The movie already exists.'
});
} else {
var rv = this.parseRawDescription(this.get('rawDescription')),
title = rv[1],
year = rv[2],
newMovie = App.Movie.create({
owner: App.Session.authUser,
ratings: [{ objectId: App.Session.objectId, value: 0 }]
title: title,
watched: false,
year: year,
});
newMovie.save();
this.setProperties({ rawDescription: '', errors: null });
}
}
}
});
I'm showing it as working, are you sure App.Session.authUser is populated? if you try console.log(newMovie.get('owner.username')) after you create it does it show anything?
http://emberjs.jsbin.com/AYidAdi/1/edit