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/
Related
I try to create a custom component for my application with emberJS, I have followed the quickstart tutoriel and now I try to create a upload button as a component.
It seems I don't have code issues but my component don't render on my main page. I have use the ember component generator to create it
here my upload-button.hbs :
<button type="button">{{#buttonText}}</button>
now my upload-button.js :
import Component from '#ember/component';
export default Component.extend({
actions: {
showExplorer(){
alert()
}
}
});
for the moment I simply put a alert() method in showExplorer()
and now my main page, application.hbs :
<upload-button #buttonText="Uploader un fichier" {{action "showExplorer"}}/>
{{outlet}}
I expect to see my button but I just have a blank page with no code error.
I suspect that's a really stupid mistake but I can't find it.
Glad you decided to try out Ember.js :) Your upload-button.hbs and upload-button.js file looks good. However, there are a couple of issues here.
The component, when invoked using the angle-bracket syntax (<... />), the name should be CamelCased to distinguish between HTML tags and Ember components. Hence, we need to invoke the upload-button component as <UploadButton />.
You defined your action, showExplorer, inside the component (upload-button.js) file, but, referenced in the application.hbs file. The data in the backing component file can only be accessed inside the component's .hbs file because of the isolated nature of the component architecture. Also, we can only attach an action using {{action}} modifier to a DOM element and not to the component itself. So,
we need to remove the action binding from application.hbs file,
{{!-- application.hbs --}}
<UploadButton #buttonText="Uploader un fichier"/>
and add the same inside the upload-button.hbs file:
{{!-- upload-button.hbs --}}
<button type="button" {{action "showExplorer"}}>{{#buttonText}}</button>
A working model can be found in this CodeSandbox
Hope you find learning Ember, a fun process!
I have created template and controller both called navbar. The code that i have in controller is simply
import Ember from 'ember';
export default Ember.Controller.extend({
isLogged: true,
});
and that in template is
{{#if isLogged}}
{{#link-to 'login' class="uk-button uk-button-danger"}}Login{{/link-to}}
{{#link-to 'signup' class="uk-button uk-button-danger"}}Join now{{/link-to}}
{{else}}
{{#link-to 'dashboard' class="uk-button uk-button-danger"}}Dashboard{{/link-to}}
{{/if}}
<button class="uk-button uk-button-large uk-button-primary uk-width-1-1" disabled={{isLogged}}>Test button</button>
The same does not seem to be working. Am i going wrong somewhere ?
The template and controller were generated using ember generator itself and the code above is the only modifications that i made.
EDIT:
Exploring the documentation, i noticed that the name of controller should be same as defined in route. Now navbar is only a template which i import using partial is there any workaround i might use for the same ?
You should use a component for this.
Use {{partial}} only if you want to render a template in the current context. Use it only if your .hbs files became to big, but use it rarely. Often a component is the better choice.
Also there you should almost never use {{render}}. You can almost always refactor it to a component.
Also using {{render}} with a model param is deprecated.
The problem was that 'partial' will only provide the template not the controllers. The correct way for this would be to use render which will import views and controllers also.
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'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 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.