I am building my first ember application and I can't get the model data to go from my songs modal to my songs template...
router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('songs');
this.route('song', {path: 'songs/:song_trackId'});
});
export default Router;
Here is my songs.js which displays properly
<div>songs</div>
{{#each model as |song|}}
{{#link-to 'song' song}}
<img height='100px' width='100px' src = '{{song.artworkUrl100}}'>
<div>{{song.trackId}}</div>
{{/link-to}}
{{/each}}
{{outlet}}
Here is my songs route..
import Ember from 'ember';
var data = {
"results" : [
{"wrapperType":"track", "kind":"song", "artistId":148662, "collectionId":528436018, "trackId":528437613,......
.
.
.
]}
export default Ember.Route.extend({
model: function(){
return data.results;
}
});
Lastly...the song template where the data should go when the user clicks
<div>HI</div>
<div> ArtistId: {{artistId}} </div>
<div> trackId: {{trackId}} </div>
<div> Kind: {{kind}} </div>
{{outlet}}
I dont think the link-to is setup improperly. Am I referencing the dynamic song incorrectly in the router?
I didn't define a song route model....
All I needed to do is set up a model in the song route as follows..
I left out the data object but in general I had to pass in the param and then grab it with the model here to find the object I was looking for with a filter
export default Ember.Route.extend({
model: function(params) {
var index = data.results.filter(function(obj){
return obj.trackId == params.song_trackId
})
var values = index[0]
return values
}
});
Related
I have an app based on Ember Data. I would like to figure out how to make the code more compact. My application looks like this:: there are several endpoints that from the Express server.
In the Ember application, each endpoint corresponds to: adapter, serializer, model, route, template. Everything works fine, but the code is too cumbersome. I'm new to Ember, and maybe there is a way to use adapters and other tools more universally. Here are some parts of my application that illustrate how it works.
localhost:5000/api/cars
localhost:5000/api/vehicles
Adapter "cars":
import RESTAdapter from '#ember-data/adapter/rest';
export default RESTAdapter.extend({
host: http://localhost:5000/api,
pathForType() {
return "cars";
}
});
Adapter "vehicles":
import RESTAdapter from '#ember-data/adapter/rest';
export default RESTAdapter.extend({
host: http://localhost:5000/api,
pathForType() {
return "vehicles";
}
});
Serializer "cars":
import RESTSerializer from '#ember-data/serializer/rest';
export default RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
payload = {
cars: payload
};
return this._super(store, primaryModelClass, payload, id, requestType);
},
primaryKey: '_id'
});
Serializer "vehicles":
import RESTSerializer from '#ember-data/serializer/rest';
export default RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
payload = {
vehicles: payload
};
return this._super(store, primaryModelClass, payload, id, requestType);
},
primaryKey: '_id'
});
Car model:
import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
name: attr("string"),
body: attr("array"),
date: attr('date')
});
Vehicle model (the same as car!):
import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
name: attr("string"),
body: attr("array"),
date: attr('date')
});
Cars index route:
import Route from '#ember/routing/route';
export default class CarsIndexRoute extends Route {
model() {
return this.store.findAll("car");
}
}
Vehicles index route:
import Route from '#ember/routing/route';
export default class VehiclesIndexRoute extends Route {
model() {
return this.store.findAll("vehicle");
}
}
Hbs templates are completely similar.
cars/index.hbs (and vehicles/index.hbs):
{{#each #model as |item|}}
<h3>{{item.name}}</h3>
<p>{{item.body}}</p>
{{/each}}
The code clearly shows that the structure is the same, and the differences are only in one parameter, which corresponds to the model name and the "end" of the api endpoint.
Can someone tell me how to organize everything more correctly, in the tradition of Ember?
Thanks!
The great thing about Ember is that it's convention based. In your case that means once /cars and /vehicles sit at the same endpoint (which they do) and have the same structure (which they seem to do as well) you simply need a single adapter and serializer for all of them.
import RESTAdapter from '#ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
host = "http://localhost:5000";
namespace = 'api'
}
import RESTSerializer from '#ember-data/serializer/rest';
export default class ApplicationSerializer extends RESTSerializer {
primaryKey = '_id';
// not sure exactly what happens in the payload but I am pretty sure you'd be able to generalize it correspondingly
}
as for models and routes I'd leave them as they are or extracted a superclass if needed.
I has many relation in my ember app but it is not working recursively.
here is my model.
-->tree.js
export default DS.Model.extend({
parent : DS.attr(),
parentId : DS.attr('number', {defaultValue: "0"}),
childrens: DS.hasMany('menu', {async:true}),
name : DS.attr(),
});
route code-->
export default Ember.Route.extend({
model: function() {
return this.store.findAll('treeview');
}
});
hbs-->>
{{#each model as |tree|}}
<div class="col-lg-12">
{{#unless tree.parentId}}
{{tree.name}}
{{#if tree.childrens}}
{{#each tree.childrens as |childmenu|}}
<div class="col-lg-12" style="margin-left:30px;">
{{sub-tree-view tree = tree}}
</div>
{{/each}}
{{/if}}
{{/unless}}
</div>
{{/each}}
sub-tree-view component js file -->
showRecord:function(){
let self = this;
let tree = self.get('tree');
var test = tree.get('childrens'); }.property(),
But it not calling the model again with children id .
With hasmany relation it should call the menu model recursively but it is not making the request again. How to deal with the has many relationship data.?
I have an Ember component checkout-form that contains some logic for handling a checkout process. Here’s a simplified version of how I’m using it:
{{#checkout-form}}
{{#each model.courses.otherDates as |date|}}
{{course-date model=date selectDate=(action selectDate) }}
{{/each}}
{{/checkout-form}}
Inside of my checkout-form.js component I have the following action:
selectDate(day) {
this.set("startAt", day.get("serverString"))
}
And finally inside of my course-date.js component I have:
click () {
const courseStart = this.get('courseStart')
this.get('selectDate')(courseStart)
}
However, when running this code I get the error:
ember.debug.js:19818 Assertion Failed: Action passed is null or undefined in (action) from <site#controller:checkout/date::ember389>.
What am I missing here? I am passing the action into the course-date component and not sure why is it asking for a controller?
Reason for the error is,
You are accessing selectDate which is undefined in that scope(ie., controller) If you do {{log 'selectDate value is ' selectDate}} inside that checkout-form which will print selectDate value is undefined. so if you want to access any properties, actions which are defined in the component then that component should yield those values.
Here is twiddle which demonstrates how you can yield action from component.
application.hbs
{{#checkout-form as |selectDate| }}
{{!--
here context is controller not the checkout-form component
Whatever you want to access from component, then component should yield those values.
--}}
{{course-date selectDate=(action 'selectDateInController')}}
{{course-date selectDate=selectDate}}
{{/checkout-form}}
application.js
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
actions:{
selectDateInController(){
console.log(' selectDate in controller');
}
}
});
templates/components/checkout-form.hbs - Here we are yielding selectDate action
{{yield (action 'selectDate')}}
components/checkout-form.js
import Ember from 'ember';
export default Ember.Component.extend({
actions:{
selectDate(){
console.log(' selectDate in checkout-form component');
}
}
});
course-date.hbs - Here we are just using the closure action that is passed to this component.
<button {{action selectDate}}> CourseDate </button>
I started an adventure with Ember a few weeks ago.
I have solid progress thanks to docs and example around the internet.
Sadly I hit a solid wall with this one as have almost copy-pasted models out of which most work and one and only one does not.
The error that I see in Inspector is:
Encountered a resource object with type "series", but no model was found for model name "series" (resolved model name using 'my-app#serializer:-json-api:.modelNameFromPayloadKey("series"))
Error while processing route: serie.index data is null...
I'm using mirage fixtures with success
// mirage/fixtures/files.js
export default [
{duration:'1',filename:'1.mkv',size:'1',id:'1',url:'dl/1.mkv'},
{duration:'2',filename:'2.mkv',size:'2',id:'2',url:'dl/2.mkv'}
];
// mirage/fixtures/series.js
export default [
{type:'show',title:'ser1',summary:'123',id:'11'},
{type:'show',title:'ser2',summary:'234',id:'12'}
];
Both use the same model for mirage
// mirage/model/file.js
// mirage/model/serie.js
import { Model } from 'ember-cli-mirage';
export default Model.extend({
});
I load fixtures this way:
// mirage/scenarios/default.js
export default function(server) {
server.loadFixtures();
}
And serializer is set on mirage this way:
// mirage/serializers/application.js
import { JSONAPISerializer } from 'ember-cli-mirage';
export default JSONAPISerializer.extend({
});
the only thing that I added to the config is
// added to mirage/config.js
this.namespace = 'api';
this.get('/series');
this.get('/series/:id');
this.get('/files');
this.get('/files/:id');
There is nothing more to do with mirage so let's move onto ember.
// app/adapters/application.js
application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api'
});
Both use the same component
// app/components/file-view.js
// app/components/serie-view.js
import Ember from 'ember';
export default Ember.Component.extend({
});
Models are defined this way:
// app/models/file.js
import DS from 'ember-data';
export default DS.Model.extend({
filename: DS.attr(),
url: DS.attr(),
art: DS.attr()
});
// app/models/serie.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr(),
type: DS.attr(),
summary: DS.attr()
});
// app/router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('serie', function() {
this.route('show');
});
this.route('file', function() {
this.route('show');
});
});
export default Router;
Routes are almost identical
// app/routes/file.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('file');
}
});
// app/routes/serie.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('serie');
}
});
Same goes for the templates
// app/templates/file.hbs
<h2>Files</h2>
{{#each model as |fileUnit|}}
{{file-view file=fileUnit}}
{{/each}}
{{outlet}}
// app/templates/serie.hbs
<h2>Series</h2>
{{#each model as |serieUnit|}}
{{serie-view serie=serieUnit}}
{{/each}}
{{outlet}}
And last are the component templates:
// app/templates/components/file-view.js
<div>
<img src="cover.jpg" width=200 hight=200 alt="">
<h3>{{file.filename}} id: {{file.id}}</h3>
</div>
// app/templates/components/serie-view.js
<div>
<h3> {{serie.title}} id: {{serie.id}}</h3>
Summary: {{serie.summary}}
</div>
And as http://localhost:4200/file works fin the http://localhost:4200/serie throw an error
I tried to tackle this by removing the unnecessary code and models so that I could narrow down the problem but ended up having two models that are very similar with almost copy-pasted functionality yet only one working.
I really have no idea what is this about anymore.
Your error gives you a hint
Encountered a resource object with type "series", but no model was found for model name "series" (resolved model name using 'my-app#serializer:-json-api:.modelNameFromPayloadKey("series"))
The problem is that ember knows how to switch between files and file but not between series and serie because the word series is irregular (both singular and plural) so serie is not its proper singular form.
Override modelNameFromPayloadKey method in your serializer to return the proper model name for the key 'series':
export default DS.JSONAPISerializer.extend({
modelNameFromPayloadKey(key) {
// if payload model name is 'series', use 'serie'
if (key === 'series') {
return 'serie';
}
// otherwise do the default thing
return this._super(...arguments);
}
});
I have two simple ember components; a list component and a list-item component. Data gets passed as an array to the list where it runs an each loop and makes a list-item component for each item in the array.
I'd like to, within the list-item component, take the data being passed to it from its parent list component and overwrite it. Eventually I would like to do more than just overwrite it and use it as a parameter in a function to return a new, parsed, value.
For the sake of example, lets say that this is a list of tweets.
Here is my code.
ROUTER.JS
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('tweets');
});
export default Router;
TEMPLATES/TWEETS.HBS
{{tweet-list tweets=model}}
ROUTES/TWEETS.JS
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return[{
tweetText: "this is an example #request tweet text1 #user"
},{
tweetText: "tweet of the #text2 how #cool"
}, {
tweetText: "tweet toot took text3"
}];
}
});
COMPONENTS/TWEET-LIST/COMPONENT.JS
import Ember from 'ember';
export default Ember.Component.extend({
});
COMPONENTS/TWEET-LIST/TEMPLATE.HBS
<ul>
{{#each tweets as |tweet|}}
<li>{{tweet-item tweet=tweet}}</li>
{{/each}}
</ul>
COMPONENTS/TWEET-ITEM/COMPONENT.JS
import Ember from 'ember';
export default Ember.Component.extend({
// model(){
// return "over written value here"
// }
});
COMPONENTS/TWEET-ITEM/TEMPLATE.HBS
{{tweet.tweetText}} - <!-- {{overwritten value here}} -->
I believe I have to do the overwriting in the COMPONENTS/TWEET-ITEM/COMPONENT.JS file ? How do I go about overwriting or, even better, returning a new value based off of the data passed down from the parent component?
Use different component properties for given and overwritten tweets. For example:
// components/tweet-item/component.js
import Ember from 'ember';
export default Ember.Component.extend({
// given tweet
tweet: null,
// overwritten tweet
parsedTweet: Ember.computed('tweet', function() {
return {
tweetText: this.get('tweet').tweetText + ' #overwritten'
};
}),
// you may also modify given tweet here
// but the better approach to send action up
// in favor of data-down-action-up principe
actions: {
publish: function(tweet) {
this.sendAction('publish', tweet);
}
}
});
// components/tweet-item/template.hbs
tweetText: {{parsedTweet.tweetText}}
<button {{action 'publish' tweet}}> Publish </button>
// components/tweet-list/component.js
actions: {
publish: function(tweet) {
// your logic
}
}
// components/tweet-list/template.hbs
<ul>
{{#each tweets as |tweet|}}
<li>{{tweet-item tweet=tweet publish='publish'}}</li>
{{/each}}
</ul>