I try to extend a component (https://github.com/ember-addons/ember-forms) to make it possible to add extra button next to the form controls.
The idea
developer passes an extra property to the component, and a partial will be rendered next to the form control (input, select, textarea).
Problem
It works fine but if i have a partial with some action, the action wont fire.
JsBin
Here is a simplified JsBin which demonstrates the problem: http://jsbin.com/pexolude/105/edit
html
<script type="text/x-handlebars">
<h2>Component test</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{booh-whaa partial="somebutton"}}
<h3>This partial's action works</h3>
{{partial "somebutton"}}
</script>
<script type="text/x-handlebars" data-template-name='_somebutton'>
<button {{action "booh"}} >Hit me!</button>
</script>
<script type="text/x-handlebars" data-template-name='components/booh-whaa'>
<h3>This is my component</h3>
{{partial partial}}
</script>
JS.
App = Ember.Application.create();
App.IndexController = Ember.Controller.extend({
selectedCategory:null,
actions: {
booh: function() {
alert("booh!");
}
}
});
App.BoohWhaaComponent = Ember.Component.extend({
});
As Knownasilya said, actions inside a component are, by default, handled by the component. However, If you specify the target on the action helper, the action with automatically propagate and you don't have to use sendAction().
In your case, this is as simple as:
{{action 'booh' target='controller'}}
Or if the action is on the route's view:
{{action 'booh' target='parentView'}}
Another option is to use Em.TargetActionSupport to send actions with contexts and other arguments to specific targets throughout your app.
When you fire an action inside a component, that component handles the action. If you want it to continue, use this.sendAction('action') and then on your component set {{booh-whaa action='booh'}}. See the guides for more information about actions in components.
Here's a working jsbin.
Also partials no longer require an underscore.
Related
I have an ember application which works fine. But user's interaction does some DOM insertion like below...
$(.test).append(<a {{action "getData"}}>Get Data</a>);
The problem is that Ember seems do not recognize that an action "getData" has been added to the DOM. Nothing is happening when I click the element. Any thoughts on this?
Another way I am trying to do is:
//create the element
$(.test).append(<a id="idnum">Get Data</a>);
//make click listener
$('#idnum').click(function(){
console.log("get data");
}
my question is where should i place the code inside the component so the it can listen on the click event. Thanks.
You should do it in Ember way. Try handlebars {{#if}} helper to render an element dynamically.
{{#if canGetData}}
<a {{action "getData"}}>Get Data</a>
{{/if}}
Here you can set the value of the canGetData to true in the controller based on the users action.
The first example can't work because ember does not analythe the Handlebars elements in the DOM, but rather parses your Handlebars template with HTMLBars, which is a full HTML parser, and then renders it manually by inserting elements, not text into the DOM.
However the second example is the way to go if you have to rely on external code that does manual DOM manipulation. And it does work. Checkout this twiddle.
This does work:
this.$('.target').append('<a id="idnum">Get Data</a>');
this.$('#idnum').on('click', () => {
alert('clicked');
});
Just make sure that the DOM is ready. So do it in the didInsertElement hook or after the user clicked a button or so.
Like Lux suggested avoid DOM manipulation. I prefer the following approach,
if it is dynamic then you can consider wrapping DOM element as a new component and use component helper.
find sample twiddle
In application.js
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
linksArray:[ Ember.Object.create({value:'Text to display',routename:'home'}),
Ember.Object.create({value:'Text to display2',routename:'home'})],
actions:{
addItem(){
this.get('linksArray').pushObject(Ember.Object.create({value:'AddedDynamically',routename:'home'}));
}
}
});
in Application.hbs
<h1>Welcome to {{appName}}</h1>
<br>
{{#each linksArray as |item|}}
{{component 'link-to' item.value item.route }}
{{/each}}
<button {{action 'addItem'}}>Add Item</button>
<br>
{{outlet}}
<br>
<br>
I have a component that is wrapping content defined by another template. I want an action on the template to trigger a method in my surrounding component. Is this possible?
Here is what I have for my template. Note this is shortened for brevity.
{{#drop-down}}
<div class="menu-selector clickable" {{action "toggleDropdown"}}>
</div>
{{/drop-down}}
This is my component:
DropDownComponent = Ember.Component.extend
showDropdown: false
actions:
toggleDropdown: ->
#toggleProperty 'showDropdown'
`export default DropDownComponent`
I can verify that everything else in my component is working. If I put the action in my component that loads this template, it works fine. But that's not where I want it.
So you would like to send an action to particular components. Take a notice that
A component is a custom HTML tag whose behavior you implement using JavaScript and whose appearance you describe using Handlebars templates. They allow you to create reusable controls that can simplify your application's templates.
and
An Ember.Component is a view that is completely isolated.
You are probably using wrong tool here. You should use instead custom view.
App.DropdownView = Ember.View.extend
showDropdown: false
elemendId: 'dropdown'
actions:
toggleDropdown: ->
#toggleProperty 'showDropdown'
return;
{{#view 'dropdown'}}
<div>
<div class="menu-selector clickable" {{action "toggleDropdown"}}>
</div>
{{/view}}
Then you can send an action to view by
Ember.View.views.dropdown.send('toggleDropdown');
Demo: http://jsbin.com/zoqiluluco/1/
I'm at my wit's end trying to accomplish what should be very straightforward behavior: I have an Ember table component (from Addepar), I would like to have buttons inside that table that trigger a modal dialog.
Since I'm new to Ember, I started with the Ember table starter kit jsbin available here: http://jsbin.com/fasudiki/9/edit
I added a custom cell view so I can use my own template:
columns: function() {
var firstColumn;
firstColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 350,
textAlign: 'text-align-right',
headerCellName: 'Column 1',
tableCellViewClass: 'App.EmberTableMyCustomCell',
getCellContent: function(row) {
return row.get('myRandomValue').toFixed(2);
}
});
return [firstColumn];
}.property(),
with
App.EmberTableMyCustomCell = Ember.Table.TableCell.extend({
templateName: 'custom-table-cell',
classNames: 'custom-table-cell'
});
and
<script type="text/x-handlebars" data-template-name="custom-table-cell">
<span class="ember-table-content">
{{ view.cellContent}}
<button {{action 'openModal' 'modal'}}>This one doesn't</button>
<button {{action 'myCellAction' 'modal'}}>This one doesn't either</button>
</span>
</script>
I then tried following the official Ember guide for modal dialogs: http://emberjs.com/guides/cookbook/user_interface_and_interaction/using_modal_dialogs/
In Ember terminology, I'd like to be able to trigger an action on the Index route from within the ember-table component.
I tried triggering the action directly from the template which didn't work:
<button {{action 'openModal' 'modal'}} >Open modal</button>
I then tried what is suggested in the "Sending Actions From Components To Your Application" guide:
http://emberjs.com/guides/components/sending-actions-from-components-to-your-application/
by creating an 'actions' map on the App.EmberTableMyCustomCell view and then using both
this.send('openModal', 'modal');
and
this.sendAction('openModal', 'modal');
Again, no success.
I then tried what's recommended in this SO question:
Ember component sendAction() not working
by setting the action name in a custom attribute on my ember-table and using it at a parameter for triggerAction(...) using:
<div class="table-container">
{{table-component
hasFooter=false
columnsBinding="columns"
contentBinding="content"
myCustomActionName="openModal"
}}
</div>
and
actions : {
myCellAction : function () {
this.triggerAction('myCustomActionName', 'modal');
}
}
Again, no success.
Any ideas what I'm doing wrong?
I have put the code in jsbin: http://jsbin.com/yovikaviseve/2/edit
I believe that (unfortunately) your action in App.EmberTableMyCustomCell is not being called. I'm not sure if it's the best solution, but I was able to work around the problem by extending Ember.Table.EmberTableComponent and defining my action there. Once the action was called there, I was able to use the method from your linked SO post to propagate the action to the ApplicationController.
I actually sent the primary action instead to make things a bit simpler, as described here: http://emberjs.com/guides/components/sending-actions-from-components-to-your-application/.
Here's the working example: http://emberjs.jsbin.com/yemebu/3/edit.
Thanks for including a JS Bin - made it much easier for me to take a look. Let me know if you have more questions or if this approach doesn't work for you!
I want to toggle a single form (see picture below) using views.
My code basically works when i add new formset it adds an empty bracket to json and then i
print the json out using eachloop
View template
{{#each view.anotherField}}
<div class="panel panel-default">
{{action 'toggleView' 'toggleViews' target='view'}}
...
</div>
{{#unless view.toggleViews}}
...content to toggle...
{{/unless}}
View controller??
actions: {
showMoreFields: function(){
this.get('anotherField').pushObject({name: ''});
,
toggleView: function(param){
this.toggleProperty(param);
}
In this given picture u can see ive toggled organization view to truth what i would like is to toggle only the clicked part not all of the forms. Is there a solution ?
Cheers,
Kristjan
If I understand correctly you need to handle events for specific parts/forms of your view. To achieve this there are at least three approaches,
1.Use the {{action}} helper passing the object you want to modify. Then in your function modify a property of that object and reflect that in your template e.g. toggle the form. Maybe in your case it could be something like ,
....
{{#each field in view.anotherField}}
<div class="panel panel-default">
{{action 'toggleView' field target='view'}}
....
2.Make a sub view/template (e.g. SubFormView) to accomodate each of your forms and handle the event of toggle within this view. Then include this via the {{view}} helper within the template of your main view.
3.Use pure js DOM handling (no {{action}} helper) and call your ember components from there.
Example of approaches 1 and 3 can be found here,
http://emberjs.jsbin.com/acUCocu/1
hbs
<script type="text/x-handlebars" data-template-name="index">
<i>using <b>{{action}}</b> helper</i>
<ul>
{{#each color in model}}
<li {{action 'test' color}}>{{color.name}}</li>
{{/each}}
</ul>
<i>using pure js DOM event handling</i>
<ul>
{{#each color in model}}
<li onclick="Ember.View.views[$(this).closest('.ember-view').attr('id')].controller.send('test2',this)">{{color.name}}</li>
{{/each}}
</ul>
</script>
js
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return colors;
},
actions:{
test:function(param){
alert('this is color:'+param.get('name')+" ("+param+")");
},
test2:function(elem){
alert('from pure js, clicked element: '+elem);
$(elem).css('color','red');
}
}
});
App.Color = Ember.Object.extend({
name:null
});
var colors=[];
colors.pushObject(App.Color.create({name:'red'}));
colors.pushObject(App.Color.create({name:'green'}));
colors.pushObject(App.Color.create({name:'blue'}));
I have ember routes set up like so:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" }, function() {
this.resource('link', { path: '/:link_id'} );
});
});
But I want the to view each link in a completely separate template. In other words, I want to render a different block of html, rather than render the link html into subreddit's {{outlet}}.
When you hit /r/xxx in the browser Ember will look for two templates related to subreddit. First it looks for 'subreddit' and then it looks for 'subreddit/index'. If 'subreddit' if found, then it is rendered and then if 'subreddit/index' is also found, it is rendered into the {{outlet}} of 'subreddit'. If 'subreddit' is not found Ember will just move on to 'subreddit/index'. The 'subreddit' template is also rendered for any sub-paths that are nested under /subreddit, such as the 'link' template for /r/xxx/yyy. 'subreddit' is kind of like a "per model layout" template that allows you to wrap (decorate) all of the sub-path templates.
Something like this should let you keep your nested routes, and allow you to use Ember default behavior.
<script type="text/x-handlebars" data-template-name="subreddit">
<p>A static header bar for this subreddit could go here, or not.</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="subreddit/index">
<p>
All of the details of the subreddit go here, this will be rendered
into the {{outlet}} of "subreddit".
</p>
</script>
<script type="text/x-handlebars" data-template-name="link">
<p>
Link view goes here. This template will replace "subreddit/index"
in the {{outlet}} of "subreddit".
</p>
</script>
If you want to render the links in separate templates you should not nest your routes:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" });
this.resource('link', { path: '/:link_id'});
});
A different approach you can take could be to use named outlet's and override renderTemplate of the corresponding route and render the template in the place of the named outlet's, have a look here for more info on that: http://emberjs.com/guides/routing/rendering-a-template/
Hope it helps.