emberjs Cannot clone an Ember.Object that does not implement Ember.Copyable - ember.js

I am using ember 1.3.1 and ember-data 1.0.0-beta.5. On creating new mode I get following error
Assertion failed: Cannot clone an Ember.Object that does not implement Ember.Copyable
Following is my model code
App.myModel = DS.Model.extend({
name : DS.attr('string'),
age : DS.attr('string')
});
In my create route model function
return Em.Object.create({});
and finally on save I do following
this.store.createRecord('property', this.get('model'));
Although despite the error, my backend service is called successfully and new model is saved.
Please guide.
Thanks

I had the same issue which I fixed by doing the following:
In the model function of the route replace
return Em.Object.create({});
with
return this.store.createRecord('myModel');
and on save replace
this.store.createRecord('myModel', this.get('model'));
with
this.get('model').save();

For the sake of completeness, in the scenario described by #acidleaf this is the solution offered by Yehuda Katz from the ember core team in this video:
Off the Menu: Building a Client-Side With Ember and Rails - Yehuda Katz # Rails Israel 2013
In the route from which you're returning a list of resources to display (i.e the plural version of the resource StoriesRoute, PostsRoute, etc..), you'll returned a filtered list containing those which are not new:
model: function() {
this.store.find('myModel');
return this.store.filter('myModel',function(myModel){
return !myModel.get('isNew');
});
}

I am quite new to Ember and still trying to catch all problems caused when migrating to newer versions of Ember and Ember Data, but...
On one hand I think you have a mistake in last code block and that it should be:
this.store.createRecord('myModel', this.get('model'));
// myModel instead of property
But on the other hand I dont think this will be the problem :-/
anyway, try to look (and compare) to changes for Ember data here: https://github.com/emberjs/data/blob/master/TRANSITION.md
and also on this http://discuss.emberjs.com/t/createrecord-using-this-get-model-throws-an-error/3968 or similiar
hope it helps!
J.

I have ran into this problem while learning Ember. The accepted answer works, but it first creates a new empty record in the store. This was not desired in my application as it displays the empty record in my view.
My Solution
Router
App.ItemsNewRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', {});
}
});
Controller
App.ItemsNewController = Ember.ObjectController.extend({
actions: {
save: function() {
this.store.createRecord('item', {
title: this.get('newTitle'),
category: this.get('newCategory')
}).save();
this.transitionToRoute('items');
}
}
});
Template
<script type="text/x-handlebars" data-template-name="items">
<ul class="list-group">
{{#each}}
<li class="list-group-item">{{title}} - {{category}}</li>
{{/each}}
{{outlet}}
<li class="list-group-item">{{#link-to "items.new"}}Add{{/link-to}}</li>
</ul>
</script>
<script type="text/x-handlebars" data-template-name="items/new">
<li class="list-group-item">
{{input class="form-control" value=newTitle placeholder="Title"}}
{{input class="form-control" value=newCategory placeholder="Category"}}
<button class="btn btn-default" {{action "save"}}>Save</button>
</li>
</script>

Related

Ember - #each pass the instance of the model

For a small webapp I'm trying to do the following:
I have a list of objects (achievement-model)that's being served through a json api
Router
export default Ember.Route.extend({
model:function(){
return this.store.find('achievement');
});
});
Model
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
});
Template
{{#each a in model}}
<div>
<h4>{{a.name}}</h4>
<p>{{a.description}}</p>
<button {{action 'addThis'}}/>
</div>
{{/each}}
The setup of the app is that there is a list of achievements. I want one list of achievements in a database. Every user that logs in can add with the button his own achievements to his profile. If a user logs in he should see the list of all the achievements but the one he already added to his profile should have a green background color and the button removed. I know this can be done with if-statements etc.
The problem however is, how do i pass the specific model to the controller so i can log this to the userprofile? I tried the following:
<button {{action 'addThis' a}}/>
and then in the controller
actions:
addThis: function(obj){
console.log(obj);
});
which logs the object, but somehow I can't acces it to get let's say the name or id to copy it to the user-profile.
I also don't know if this is the best approach for what I'm trying to achieve?
Edit
I think this has something to do with promises. I can see the data is logged in the above console.log. I just don't know how to target it. it's wrapped in _data. I tried the afterModel to wait untill everything's loaded, but that doesn't seem to work.
What you could is to use an ItemController, e.g. which handles each item in the ArrayController,
e.g.
{{#each a in model itemController="achievement"}}
<div>
<h4>{{a.name}}</h4>
<p>{{a.description</p>
<button {{action 'addThis'}}/>
</div>
{{/each}}
Since the itemController is "achievement", by naming convention, the controller becomes
App.AchievementController = Ember.ObjectController.extend({
init: function() {
var name = this.get('name');
var description = this.get('description');
}
});

Emberjs: Accessing parent route model which is a Promise (master-detail)

I've got master-detail page layout as on image. I access this page through #/masters/:master_id app url.
Routes a defined as follows:
App.Router.map(function() {
this.resource('masters', { path: '/masters' }, function() {
this.route('detail', { path: '/:master_id' });
});
});
App.MastersRoute = Ember.Route.extend({
model: function() {
return App.DataStore.getData('/api/masters'); //returns Promise!
},
setupController: function(controller, model) {
controller.set("content", model);
}
});
App.MastersDetailRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor("masters").find(function(item) {
return item.get("id") == params.master_id;
});
}
});
Templates:
<script type="text/x-handlebars-template" data-template-name="masters">
<div id="masters-grid">
{{#each master in model}}
<div {{action "show" master}}>
{{master.name}}
</div>
{{/each}}
</div>
<div id="detail">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars-template" data-template-name="masters/detail">
{{model.name}} <br />
{{model.age}} <br />
{{model.address}} <br />
</script>
When clicking through masters in the grid I want to show their details in Detail outlet and I do not want to reload all masters from API when changing the master selection.
I have a problem with MastersDetailRoute's model, because this.modelFor("masters") returns undefined. I think, it is caused by returning Promise in model hook. Any idea or workaround how to access one item from Masters model or controller in "child route" model hook?
I see a few things here.
when defining routes that have the same url as the route name theres no need to specify the path
the detail route should also be a resource as it is a route backed by a model
In the Masters route returning a promise is correct and supported natively by ember. The route wont be resolved until the promise is.
setup controller isn't required
its usually best to do the required api call to fetch the individual record in the detail route. This will only be used when loading the page for the first time (if f5 ing or coming from a bookmark)
in your masters template you can use id instead of typing data-template-name or better still look into use ember-cli/brocolli or grunt to precompile your templates
to prevent ember refetching your model when selecting a row use the handlebars helper link-to
{{#link-to 'masterDetail' master}}
{{master.name}}
{{/link-to}}
just to clarify, using link-to in this way passes the object specified in the second parameter as the model to the specified route (first parameter). In your case master will now be set as the model to the master detail route.
in masters detail theres no need to type "model" the default context (i.e. the value of "this") in your template is the controller, then if the property is not found on the controller it looks for it in the model.
Hope this helps

Ember.js Multiple Models for a Modal Dialog Box

I am new to Ember.js, and I am building a web application that is using Ember.js and Ember-Data for its front-end technology. I am trying to understand what would be the best practice for when you might have multiple ember-data bound components on a page that use an independent model.
Here is kind of what I'm trying to do:
https://gist.github.com/redrobot5050/6e775f4c5be221cd3c42
(There's a link on the page to editing it within jsbin this gist. For some reason, I can't get a 'Share' URL off the vanity URL.)
I have a template like so:
<script type="text/x-handlebars" data-template-name="index">
<p>Options for graphics quality: </p>
<ul>
{{#each item in model}}
<li>{{item.setting}}</li>
{{/each}}
</ul>
<p>Currently Selected: {{model.selectedValue}}</p>
<p>Draw Distance Options:</p>
<ul>
{{#each item in dropdown}}
<li>{{item.distance}}
{{/each}}
</ul>
<p>Currently Selected Distance: {{selectedDistance}}
</p>
{{outlet}}
<button {{action 'openModal' 'modal' model}}>Change Settings</button>
</script>
In this template, all the data binds correctly and appears in scope. However, when I attempt to modify it within its modal dialog box, only Quality is bound to its Ember.Select view. I have attempted to force the binding in the IndexController with a controller.set but it does not appear to be working.
My IndexController looks like this:
App.IndexRoute = Ember.Route.extend({
model: function() {
var qualityList = this.store.find('quality');
console.log('qualityList=' + qualityList);
return qualityList;
//return App.Quality.FIXTURES;
},
setupController : function(controller, model) {
controller.set('model', model);
var drawDistanceList = this.store.find('drawDistance');
console.log('distanceList=' + drawDistanceList );
controller.set('dropdown', drawDistanceList);
controller.set('selectedDistance', 1);
//this.controllerFor('modal').set('dropdown', drawDistanceList );
}
});
The JSBin really shows off what I am attempting to do: I want to load/bind each of the drop downs independently from the same controller. The JSBin does not work correctly because I'm not really sure how to do this, just yet. I am posting to stackExchange to see if someone can modify this JSBin and show me what I'm doing wrong, or if someone can point me in the right direction, design-wise on how to solve this problem?
(For example, I think a possible solution could be to create the dropdowns as components, and load the data through their controller or pass it in as properties from the parent controller, but I really want to know what is the "The Ember Way").
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
qualityList: this.store.find('quality'),
drawDistanceList: this.store.find('drawDistance')
});
},
setupController: function(controller, model) {
controller.set('model', model.qualityList);
controller.set('dropdown', model.drawDistanceList);
}
});
Documentation for Ember.RSVP.hash used to be here: http://emberjs.com/api/classes/Ember.RSVP.html#method_hash. I'm not sure why it has disappeared.
For the moment, you can find it at: http://web.archive.org/web/20140718075313/http://emberjs.com/api/classes/Ember.RSVP.html#method_hash

Are query parameters working at all?

So I want to use query parameters in the URL of my application. The Ember guide describes the solution: http://emberjs.com/guides/routing/query-params/
Unsuccessfully I tried it out in my ember-cli project, so I've set up a small test project without cli.
Route:
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
Template:
<script type="text/x-handlebars" id="index">
<ul>
{{#each item in model}}
<li {{action 'pickColour' item}}>{{item}}</li>
{{/each}}
</ul>
<div>Currently selected: {{selected}}</div>
</script>
Controller:
App.IndexController = Ember.ArrayController.extend({
queryParams: ['selected'],
selected: null,
actions: {
pickColour: function(colour) {
console.log("Colour " + colour + " selected");
this.set('selected', colour);
}
}
});
According to the Ember guide this should bind the selected field of the controller to the url parameters. But in this case no url parameters is set when I click a specific colour.
It should be so simple yet I can't make it work. Am I gloriously overlooking something?
Edit: SOLUTION
I missed the fact that for now it's only available in the beta. If you read this in the future, be aware that it might be available in the latest full release.
This is working just fine in version 1.9.0, so this question can probably be closed.
Working demo here

Ember.js: ArrayController undefined in template

Problem:
I am kind of struggling with the organization of my first ember app. The current issue is that the my Items ArrayController is not defined in my dashboard template:
<script type="text/x-handlebars" data-template-name="dashboard">
{{#if controllers.items}}
<p class="alert alert-error">Dashboard can access item's info - Nice!</p>
{{else}}
<p class="alert alert-error">Dashboard cannot access items... :-/</p>
{{/if}}
</script>
Likely cause: *
**EDIT: after talking with #conrad below, I'm kind of questioning this:*
I had a similar issue in an earlier post and kingpin2k suggested the cause was that I:
"never created anything that uses the options controller".
This is probably the case here as well. This quick screencast shows that a breakpoint on my ArrayController is not hit on page load - but it is hit when I inspect the Items controller in the Ember inspector tool (eg, Ember creates the ArrayController object right then for the first time).
Apparent non-solutions:
My Dashboard controller says it needs the Items controller. I guess that isn't enough to instantiate the ArrayController?
App.ItemsController = Ember.ArrayController.extend({
len: function(){
return this.get('length');
}.property('length'),
totalCost: function() {
return this.reduce( function(prevCost, item){
return parseInt(item.get('values').findBy('type', 'cost').price, 10) + prevCost;
}, 0);
}.property('#each.values')
[more computed properties...]
});
App.DashboardController = Em.Controller.extend({
needs: ['items'],
itemsLength: Ember.computed.alias('controllers.items.len'),
itemsTotalCost: Ember.computed.alias('controllers.items.totalCost'),
[more computed properties...]
});
Furthermore, each item in Items is being rendered in my items template. I guess that does not create the missing controllers.items either...
<script type="text/x-handlebars" data-template-name="items">
{{#each}}
[these render fine]
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="display">
<!-- DISPLAY TEMPLATE -->
{{!- DASHBOARD -}}
{{render dashboard}}
{{!- ITEMS -}}
{{render 'items' items}}
</script>
So then.. what?
I can imagine many possible avenues, but haven't gotten any of them to work yet:
Specify the Items ArrayController in {{render dashboard}}?
Some configuration in a Route?
Maybe my templates/routes are not correctly arranged?
You could make sure that the ItemController is instantiated in the dashboard template by calling it in the DashboardController's init function:
App.DashboardController = Em.Controller.extend({
needs: ['items'],
init: function() {
this._super();
this.get('controllers.items.length');
}
});
/edit:
removed the part that was not helpful