Call action from parent component in Ember 2.x - ember.js

I am trying to call an action from a parent component in my route view.
mychild.hbs
{{#parent as |wrapper|}}
<button {{action "animate"}}>Login</button>
{{/parent}}
parent.hbs
<div>{{yield}}</div>
mychild route (no actions)
export default Ember.Route.extend(
});
mychild controller
export default Ember.Controller.extend({
});
parent component
export default Ember.Component.extend({
actions: {
animate() {
console.log('ok');
}
},
});
How to call animate() from my component ? What am I doing wrong ?

Changed the following code to this. Seems to be working.
<div>
{{yield this}}
</div>
{{#my-component as |mc|}}
<button {{action "doIt" target=mc}}>callDoIt</button>
{{/my-component}}

Related

How can we get the original event in ember controller action

How can we get the original event in ember action while handling action inside controller?
Controller
import Ember from 'ember';
export default Controller.extend({
showDetails:function(id)
{
// I want like $(this).position().top
}
});
Template
{{#each item in list}}
<em {{action "showDetails" item.id bubbles=false}}></em>
{{/each}}
{{#each item in list}}
<em onclick={{action "showDetails" item.id}}></em>
{{/each}}
In controller,
import Ember from 'ember';
export default Controller.extend({
actions: {
showDetails(id, event) {
//i want like $(this).position().top
}
}
});

Ember 2 action sent to route instead of component

In my app I've defined this component:
// components/buy-functions.js
export default Ember.Mixin.create({
actions: {
openModal: function() {
$('#buyModal').openModal();
}
}
});
then in the route's template:
<h5>Buy form</h5>
{{#buy-functions}}
<div class="btn" {{action "openModal"}}>Buy</div>
{{/buy-functions}}
(the component does not have a template)
But when i click the button I get the error "nothing handled the "openModal" action...
Can someone explain what I'm doing wrong here?
You need to send your action to route
openModal: function(modalName) {
this.sendAction('openModal',modalName);
}
}
change your button to
<div class="btn" {{action "openModal" 'myModal'}}>Buy</div>
and then in your route
openModal: function(modalName) {
//do whatever you want
}
But another way would be:
let's change your component to
// components/buy-functions.js
export default Ember.Component.extend({
actions: {
openModal: function() {
$('#buyModal').openModal();
}
}
});
then create your component template
// tempaltes/components/buy-functions.hbs
<div class="btn" {{action "openModal"}}>Buy</div>
and then in your route template only use your component name
{{buy-functions}}
I wrote these codes on the fly. hope it works for you.
There is an add-on (ember-route-action-helper) that provides a helper (route-action) for just this very use case.
There's a blog about it.

How to find and delete a specific record in Ember

I am totally newbie in Ember. I would like to find a record in todos array then delete it. I know I have to pass a parameter to function to find it first then delete it, but I had no luck. Here are related files:
todos.hbs:
{{#each todo in model}}
<li>
<label>{{#link-to "todos.show" list}}{{todo.title}}{{/link-to}}</label>
<button type="button" class="destroy" {{action "deleteTodo"}}></button>
</li>
{{/each}}
I put deleteTodo action in toods routes.
todos routes :
export default Ember.Route.extend({
model: function() {
return this.store.find('todo');
},
deleteTodo: function() {
// Function itself
});
Thanks
Move the deleteTodo method into an action on your routes controller instead of on the Route. Then pass your current todo into your controllers deleteTodo method:
<button type="button" class="destroy" {{action "deleteTodo" todo}}></button>
Looks like your are using ember-cli so the controller would look something like
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
deleteTodo: function(todo){
todo.destroyRecord();
}
}
});

Access jquery event from ember component action

I'm trying to work with a simple overlay component, and close this overlay if someone clicks outside of the overlay content:
<div class="overlay" {{action 'close' on='click'}}>
<div class="item">
<form {{action 'submit' on='submit'}}>
{{yield}}
{{#link-to closeRoute class="close"}}Close{{/link-to}}
</form>
</div>
</div>
The component looks like this:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
submit: function() {
this.sendAction();
},
close: function(param) {
console.log(param); // -> undefined
console.log(this); // -> complete component object, no reference to the event?
// this.$("a.close").click();
}
}
});
This works like advertised, however, I need to determine the target of the click event, because also clicks on the item and form will trigger this click(close) action.
Question: How can I access the (jQuery) event object which has a target from within the close action inside the component?
I am using EmberCLI, and Ember 1.9
I have found this to provide the required result:
export default Ember.Component.extend({
classNames: ['overlay-block'],
didInsertElement: function() {
var self = this;
self.$().click(function(e) {
if (self.$(e.target).hasClass("overlay-block")) {
self.$("a.close").click();
}
});
}
});
This does not use an ember action like I expected. I'll leave the question open for a while to see if somebody comes up with an more 'Ember way' of doing this.
More Ember way
export default Ember.Component.extend({
classNames: ['overlay-block'],
click: function(e) {
if (this.$(e.target).hasClass("overlay-block")){
this.$("a.close").click();
}
}
});

Global CRUD Ember.js

I was wondering if someone could give me brief direction. I'm making an app that I want to be able to take notes from anywhere I'm at in the app (CRUD). I'm rendering my presentations in my application controller using {{render}} but I'm not sure how to put the full crud operations there as well. This is what I have so far:
-- Presentation Controller
import Ember from 'ember';
var PresentationController = Ember.ObjectController.extend({
actions: {
edit: function () {
this.transitionToRoute('presentation.edit');
},
save: function () {
var presentation = this.get('model');
// this will tell Ember-Data to save/persist the new record
presentation.save();
// then transition to the current user
this.transitionToRoute('presentation', presentation);
},
delete: function () {
// this tells Ember-Data to delete the current user
this.get('model').deleteRecord();
this.get('model').save();
// then transition to the users route
this.transitionToRoute('presentations');
}
}
});
export default PresentationController;
-- Presentations Controller
import Ember from 'ember';
var PresentationsController = Ember.ArrayController.extend({
actions: {
sendMessage: function ( message ) {
if ( message !== '') {
console.log( message );
}
}
}
});
export default PresentationsController;
-- Model
import DS from 'ember-data';
var Presentation = DS.Model.extend({
title: DS.attr('string'),
note: DS.attr('string')
});
-- Presentations Route
import Ember from 'ember';
var PresentationsRoute = Ember.Route.extend({
model: function() {
return this.store.find('presentation');
}
});
export default PresentationsRoute;
-- Presentation Route
import Ember from 'ember';
var PresentationRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('presentation', params.id);
}
});
export default PresentationRoute;
-- Application Route
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
return this.store.find('category');
},
setupController: function (controller, model) {
this._super(controller, model);
controller.set('product', this.store.find('product'));
controller.set('presentation', this.store.find('presentation'));
}
});
-- Application HBS
<section class="main-section">
<div id="main-content">
{{#link-to "presentations.create" class="create-btn expand" tagName="button"}} Add presentation {{/link-to}}
{{render 'presentations' presentation}}
{{outlet}}
</div>
</section>
-- Presentations HBS
{{#each presentation in controller}}
{{#link-to 'presentation' presentation tagName='li'}}
{{presentation.title}}
{{/link-to}}
{{/each}}
{{outlet}}
-- Presentation HBS
{{outlet}}
<div class="user-profile">
<h2>{{title}}</h2>
<p>{{note}}</p>
<div class="btn-group">
<button {{action "edit" }}>Edit</button>
<button {{action "delete" }}>Delete</button>
</div>
</div>
Basically what you're describing is a modal of sorts. It'll be accessible no matter what page (route) you're viewing, and you will be able to perform actions within this modal (creating notes, editing notes, deleting notes, etc) without leaving or affecting the current page being displayed in the background. Essentially, what this means is that you should leave the router alone, since the router is what controls the current page, and you don't want to affect that. You're not going to want to have any {{#link-to}} or transitionTo or transitionToRoute calls, nor any presentation-related routes or outlets.
Instead, you're going to have to handle everything at the controller and view level. This is where components really come in handy, as they're great for encapsulation if you use them correctly. Inside of presentations.hbs, I'd use components to represent each of the presentations:
{{#each presentation in controller}}
{{individual-presentation presentationModelBinding="presentation"}}
{{/each}}
Note that you'll need a corresponding IndividualPresentationComponent object that extends Ember.Component. Going further, inside of individual-presentation.hbs, I'd have code similar to what you have now, but with allowances for various CRUD operations:
{{#if editing}}
{{input value=presentationModel.title}}
{{textarea value=presentationModel.note}}
{{else}}
<h2>{{title}}</h2>
<p>{{note}}</p>
{{/if}}
<div class="btn-group">
{{#if editing}}
<button {{action "save" }}>Save</button>
{{else}}
<button {{action "edit" }}>Edit</button>
{{/if}}
<button {{action "delete" }}>Delete</button>
</div>
Note that the context for a component's template is the component itself, not some other controller. Similarly, actions fired inside of a component's template are direct to the component's actions hash. So your IndividualPresentationComponent will need to look like this somewhat:
IndividualPresentationComponent = Ember.Component.extend({
classNames: ['user-profile'],
actions: {
save: function () {
this.sendAction('save', this.get('presentationModel'));
this.set('editing', false);
},
edit: function () {
this.set('editing', true);
},
delete: function () {
this.sendAction('delete', this.get('presentationModel'));
}
}
});
Notice I'm using sendAction here. This is how components communicate with the outside world. To get this to work, go back your presentations.hbs and intercept the actions like so:
{{#each presentation in controller}}
{{individual-presentation presentationModelBinding="presentation"
save="savePresentation"
delete="deletePresentation"}}
{{/each}}
Here you're basically saying that if the component sends the "save" action, you want to handle it with your controller's "savePresentation" action, and if the component sends the "delete" action, you want to handle it with your controller's "deletePresentation" action. So your presentations-controller.js will need to implement those actions:
var PresentationsController = Ember.ArrayController.extend({
actions: {
savePresentation: function (presentationModel) {
...
},
deletePresentation: function (presentationModel) {
...
},
}
});
And you can delete PresentationController, since all of its functionality is now handled directly by your IndividualPresentationComponent and your PresentationsController.