So I'm following a tutorial to build an app on CodeSchool and I was trying to figure out how to write in a toggle when I noticed an error in the console basically saying that nothing is handling the action block I wrote in the template.
"Nothing handled the action 'toggleOption model option'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble."
The code below is the part of the template I'm having trouble with.
<ul class='list list--answer'>
{{#each model.poll.option as |option|}}
<li class='list-item'>
<button class='list-item-checkbox {{if (eq model.option option) "is-selected"}}' {{action "toggleOption model option"}}>
<b class='srt'>Select</b>
</button>
<span>{{option.label}}</span>
</li>
{{/each}}
</ul>
This is the route associated with the template.
import Ember from 'ember';
export default Ember.Route.extend({
store: Ember.inject.service(),
model(){
const poll = this.modelFor('polls.poll');
return this.get('store').createVote(poll);
},
actions: {
toggleOption(vote,option){
vote.toggleOption(option);
}
}
});
Anyways, is there something I'm missing? I've been staring at this for awhile and I couldn't figure this out. The tutorial video I've been following and their completed code doesn't seem to run into this issue either.
There is syntax issue {{action "toggleOption model option"}} it should be {{action "toggleOption" model option}}.
Related
I am creating one ember app.
Flow is like " page1 displays list of feeds item and clicking on any of the feed will take user to page2 showing details about that feed"
What i am doing:
i have one component named app-feed. Template is as below
<div onclick={{action 'click' feed}}>
{{#paper-card class="card-small" as |card|}}
<!-- --> {{card.image src=feed.imagePath class="small-feed-img" alt=feed.title}}<!---->
{{#card.header class="flex-box short-padding" as |header|}}
{{#header.avatar}}
<img class="profile-small" src="http://app.com/users/{{feed.userName}}.jpg" alt="{{feed.name}}" />
{{/header.avatar}}
<span class="tag-sm like-box">
{{feed.likes}} {{paper-icon "thumb_up" size="18"}}
{{feed.commentCount}}{{paper-icon "chat_bubble" size="18"}}
</span>
{{/card.header}}
{{#card.actions class="action-block"}}
{{#paper-button iconButton=true}}{{paper-icon "favorite" size="18"}}{{/paper-button}}
{{#paper-button iconButton=true}}{{paper-icon "share" size="18"}}{{/paper-button}}
{{#paper-button iconButton=true}}{{paper-icon "shopping_basket" size="18"}}{{/paper-button}}
{{/card.actions}}
{{/paper-card}}
</div>
component.js is as below
import Ember from 'ember';
export default Ember.Component.extend({
actions:{
click(feed){
console.log("Click event fired:"+feed.id); //Output is correct in console
this.sendAction("onClick", feed); //sending onClick Action
}
}
});
I'm populating list of this component in one of my route.
Template is as below
{{#app-sidenav user=model}}{{/app-sidenav}}
<div class="content">
<div class="row">
{{#each model as |item|}}
{{#app-feed-small onClick=(action "getDetail" item) class="col-xs-5" feed=item}} {{/app-feed-small}}
{{/each}}
</div>
</div>
route.js is as below
import Ember from 'ember';
export default Ember.Route.extend({
store: Ember.inject.service(),
model(){
//Populating module. Works just fine
} ,
actions:{
getDetails(feed){
console.log("Getting details of "+feed.id);
}
}
});
I have defined getDetails action as mentioned in my template.js of the route still i am getting below error
""Assertion Failed: An action named 'getDetail' was not found in (generated feed.index controller)""
feed.index is my route.
I used same method and modified paper-chip's source to get action corresponding to click on paper-chip's item which worked. But i am not able to do same in my own component.
Please let me know what is missing
Your problem is that in your second last code snippet, the one with your template. You refer to the action as getDetail but in route.js your last code snippet you declare the action as getDetails which is different to the code in your template. It's a common spelling error, one has an "s" st the end whereas the other doesn't.
The actions should be in controllers. And if controller bubbles up then the action in route be called.
For your case you don't need controller.
You can use ember-transition-helper
I assume you have in router.js :
this.route('feeds', function(){
this.route('edit', {path: '/:id'});
});
Now your template is going to be :
{#app-sidenav user=model}}{{/app-sidenav}}
<div class="content">
<div class="row">
{{#each model as |item|}}
{{#app-feed-small onClick=(transition-to "feeds.edit" item) class="col-xs-5" feed=item}} {{/app-feed-small}}
{{/each}}
</div>
</div>
sendAction is an ancient way to calling action inside controller/route.
The new style is to use closure action, which passes action as a value by creating a closure at the time of value passing.
Yes, you are correct. The action has been sendAction is able to bubble up from,
correspond controller -> correspond route -> upper route -> ... -> application route
However, closure action does NOT bubble.
Please refer to Ember component send action to route where #kumkanillam detailed explained how to call action inside route using different method and the differences between sendAction and closure action.
I have also made a sample project and write a simple explanation for it at,
https://github.com/li-xinyang/FE_Ember_Closure_Action
I was following this Ember tutorial and this quickly got a lot more complicated. This was the tutorial that I was following.
I am lost as to what is going on. When is the index.hbs getting loaded and why? Here is my code starting with the router.js:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('todos', { path: '/'}, function() {
this.route('complete');
this.route('incomplete');
});
});
export default Router;
So it looks like our home url will load the todos.js route right? This is my code:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
let todos = [
{
title: 'Learn Ember',
complete: false,
},
{
title: 'Solve World Hunger',
complete: false,
}
];
return todos;
}
});
So this todos.js route is my model right?
I assume ember also loads the todos.hbs template by default? Is that right? Or does it load the app/templates/todos/index.hbs? Which one does it load?
This is my app/templates/todos.hbs code:
<input type="text" id="new-todo" placeholder="What needs to be done?" />
{{#todo-list todos=model}}
{{outlet}}
{{/todo-list}}
This is my app/templates/todos/index.hbs code:
<ul id="todo-list">
{{#each model as |todo|}}
<!-- this loads the component todo-item and passes in a todo as todo -->
{{todo-item todo=todo}}
{{/each}}
</ul>
The tutorial doesn't really explain what is going on here. If the index.hbs gets loaded, does it then load the todo-item component template? If so, this is my app/templates/components/todo-item.hbs:
<input type="checkbox" class="toggle" checked="{{if todo.complete 'checked'}}">
<label class="{{if todo.complete 'completed'}}">{{todo.title}}</label><button class="destroy"></button>
In the event that the app/templates/todos.hbs gets loaded...What is going on in the app/templates/todos.hbs? Are we passing in the model (somehow accessible in the template?) as todos to the todo-list component? Here is the app/templates/components/todo-list.hbs
<section id="main">
{{yield}}
<input type="checkbox" id="toggle-all">
</section>
<footer id="footer">
<span id="todo-count">
<strong>2</strong> todos left
</span>
<ul id="filters">
<li>
All
</li>
<li>
Active
</li>
<li>
Completed
</li>
</ul>
<button id="clear-completed">
Clear completed (1)
</button>
</footer>
Welcome to the wonderful world of Emberjs! first of all, I recommend you to visit the official page of Emberjs. Can you see the sidebar menu? well, be ready to spend some time reading it if you want to understand how Emberjs works. I strongly recommend you to read at least Router, Template, Component and Controller sections to begin with.
Let's see some of the snippets you have provided:
Router.map(function() {
this.route('todos', { path: '/'}, function() {
this.route('complete');
this.route('incomplete');
});
});
This is where you define your routes. Here what you have is the main route called 'todos' but used as the root page (starting at /). After it, there are two more routes: /complete and /incomplete.
model() {
let todos = [
{
title: 'Learn Ember',
complete: false,
},
{
title: 'Solve World Hunger',
complete: false,
}
];
return todos;
}
Here you are defining a model in one route (I assume is the route of todos). Pretty straight, isn't it? if you were using Ember Data for example. you would ask the server for the model here and the route would wait until receive the response.
The reason why you have an index template and a todos template is simple: todos.hbs will contain the {{outlet}} in which every page will be rendered. Imagine it as a wrapper. Whatever comes after / will be wrapped by this todos.hbs, even the index.hbs. You have more info here, in the guides (reason why I recommend you to read it first).
Let's move to another snippet:
{{#todo-list todos=model}}
{{outlet}}
{{/todo-list}}
Here you are using a component to wrap whatever is rendered in the {{outlet}}. You haven't pasted it here, but it should contain in its template at least a {{yield}} to specify where the {{outlet}} will be rendered. That info about {{yield}} can be found here.
Let's move to the next part:
ul id="todo-list">
{{#each model as |todo|}}
<!-- this loads the component todo-item and passes in a todo as todo -->
{{todo-item todo=todo}}
{{/each}}
</ul>
This {{#each}} handlebar, expressed in a block way (that's why it uses the # at the beginning and the / at the end), is a loop that allows you work with each item of your model, defined as todo. What you are doing here is to provide the component todo-item with one item of the model. If your model has 3 todos, todo-item will be rendered 3 times, one for each of them.
Again, I recommend you to follow that tutorial having the emberjs guides opened and whenever you have a doubt, check the guides until you understand the concept and then, move to the next step.
Here are my controllers:
App.DesignPhotosController = Ember.ArrayController.extend({
itemController: 'designPhoto'
});
App.DesignPhotoController = Ember.ObjectController.extend({
needs: ['designPhotos'],
toDelete: false,
actions: {
toggleDelete: function() {
this.set('toDelete', !this.get('toDelete'));
}
}
});
And my template:
{{#each}}
<ul>
<li>
{{title}}
{{#if toDelete}}
<button class="restore" {{action "toggleDelete"}}>Restore</button>
{{else}}
<button class="delete" {{action "toggleDelete"}}>Delete</button>
{{/if}}
</li>
</ul>
{{/each}}
However, when I click on the "Delete" button, I get a message logged:
Error: Nothing handled the action 'toggleDelete'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.
As far as I can tell I am doing this correctly, bit I tried forcing it by various combinations of:
Adding target=this to the action
Adding {{#each item in controller}} and target=item
Changing the action to {{action toggleDelete this}}, with and without quotes
Nothing works.
Can anyone point out what I am doing wrong?
One possible reason of this behaviour is a bug present in 1.13 (up until the very last 1.x release, 1.13.13), which breaks actions that should be processed by item controllers.
In case you can't revert to release 1.12, the second workaround from the question
changing {{#each}} to {{#each item in controller}} and adding target=item to the action
is the officially recommended one and has worked for my code, although the first one
adding target=this to the action (with simple {{#each}})
should probably work too.
(I suspect that OP left either of these in the code and didn't register that it fixed the issue, because both came up as false negatives earlier due to Firefox's very persistent caching.)
Hi i am very new to ember js. i pass action parameters(id ) on link-to action in template but i did not get the values in my controller.
My Template code as follows:
index.html:
<script type="text/x-handlebars" data-template-name="search">
{{#each model.results}}
// here i pass id value along with action
{{#link-to 'profile' id action="profileinfo"}}
</script>
app.js:
App.SearchController = Ember.ObjectController.extend({
id: '',
actions:{
profileinfo: function(id){
// Here i access id value like this
console.log(id);
var id = this.get('id');})
when i click on the link action goes to Searchcontroller, but i get id value is empty.I follow some solutions in stack overflow but unfortunately i did not get anything. Please provide some solution
I don't get why you're using the {{#link-to}} helper for triggering an action on your controller. Maybe you could simply use the {{action}} helper ?
If you try doing it that way, would it work ?
<button type="button" {{action "profileinfo" id}}>Click me !</button>
From there, your console.log(id); should get your value.
EDIT
Would also work for a <a> tag
<a href="#" {{action "profileinfo" id}}>Click me !</a>
I've created popular addon for doing just that:
{{#link-to 'profile' id invokeAction='profileinfo'}}
Simply install it:
ember installember-link-action
Please leave a star if you enjoy it or leave feedback if you feel anything is missing. :) It works with Ember 1.13 and 2.X (tested on Travis CI).
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>