is there a way to show loading template when we save a model.
i am able to use Application level loading template when transition to different route but when a model is saved at that time loading template does not show.
this.transitionTo('routeName') takes you to loading.hbs until it gets the promise from server but when doing model.save() it does not show.
The loading and error substates are only used when loading routes. There's no way to invoke them during a controller action (unless that action loads a new route, but in that case the states are still tied to loading the new route, not the action).
You can still display a loading template when executing an action by using a property and a partial:
The template:
<!-- templates/components/user-account.hbs -->
{{#if busy}}
{{partial 'loading-template-name'}}
{{else}}
{{!-- template content --}}
<button {{action "save"}}>Save</button>
{{/if}}
The component:
// components/user-account.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
save: function () {
if (this.get('busy')) {
return;
}
this.get('user').save()
.then(() => {
// handle success
})
.catch((e) => {
// handle error
})
.finally(() => {
this.set('busy', false);
});
},
},
})
Related
I have a nested component. The child-component has an input field bind with mixin variable and action, In parent-component had button action. ( without mixin because parent-component considered as a addon ) while button action triggers the child-component value update to the mixin variable. How trigger a child action from parent-component.
Note: Please refer the attached demo link
https://ember-twiddle.com/d8b01ba563b555fc01374f300db20c5b?openFiles=components.child-component.js%2C
An easier approach than triggering actions on your child-component would be to pass the updated_val down to the child component, and let the ember value-binding do the rest. When the value has changed and you click update, make your ajax call from your dialog-component.
e.g. for passing your updated_val down
//application.hbs
{{dialog-component updated_val=updated_val}}
But since your question was "How to trigger actions on a child component", this might be an approach (see updated twiddle):
//dialog-component.js
import Ember from 'ember';
export default Ember.Component.extend( {
actionCalled: false,
actions:{
callChildAction() {
this.toggleProperty( 'actionCalled' );
},
updateValue(updateVal) {
this.set('updated_val', updateVal);
}
}
});
//dialog-component.hbs
<div class='dialog'>
{{!pass your 'updateValue' action from the dialog-component to the child-component}}
{{ child-component actionCalled=actionCalled updateValue=(action 'updateValue')}}
<button {{action 'callChildAction' }}> Update </button>
</div>
//child-component.js
import Ember from 'ember';
export default Ember.Component.extend( {
child_val: '',
actionObserver: Ember.observer('actionCalled', function(){
this.send('childAction')
}),
actions:{
childAction(){
alert( 'childAction called..' );
// some validation and ajax call.
this.sendAction('updateValue', this.get('child_val'));
},
}
});
//child-component.hbs
{{input value=child_val}}
I'm trying to create a modal for my users to signin, so I have this link:
<li><a {{action "signin"}}>Sign In</a></li>
in a {{planhw-navbar}} component.
{{planhw-navbar signin=(action "showModal" name="signin-modal")}}
But when I open my browser, I get the error:
An action named 'showModal' was not found in (generated application controller)
I've tried putting an action in a controller, a route, and a component:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
showModal: function(name) {
this.render(name, {
into: 'application',
outlet: 'modal'
});
}, //...
}
})
My component, {{signin-modal}}, works correctly.
My entire application.hbs:
{{planhw-navbar signin=(action "showModal" name="signin-modal")}}
{{outlet}}
{{outlet 'modal'}}
You need to add an action called showModal in your application.js controller.
I have a map application. When clicking on a pin, I want to update a "teaser" component in the template by supplying e.g. a query parameter previewId without triggering a full re-render of the route, because that would re-initialize the map and center it on the init position, take a lot of time, in short: is ugly and bad user experience.
So what I have is a map route:
export default Ember.Route.extend({
model: function () {
return this.store.findAll('map-object-proxy');
}
});
a map controller, where I handle the query params:
export default Ember.Controller.extend({
queryParams: ['previewId'],
previewId: null,
previewObject: Ember.computed('previewId', function () {
return this.store.findRecord('map-object', 1);
})
});
and a map-panel component which gets handed the previewObject from the map.hbs template:
<div id="map"></div>
<!-- ... -->
<div class="row" id="teaser-header">
<div class="col-xs-12">{{previewObject.someProperty}}</div>
</div>
map.hbs has this Handlebars markup:
{{map-panel elementId="map-panel" objectProxies=model previewObject=previewObject}}
Sorry, I've not yet quite come to terms with ember's component architecture, even more so as controllers will be deprecated soon and somehow seem to act like a fifth wheel.
Thanks!
You can "refresh" your component by wrapping it like this:
{{#if refresh}}
//component template
{{/if}}
and then an Action to hide and show the component, forcing it to render again.
const _this = this;
this.set('refresh', false);
Ember.run.next(function () {
_this.set('refresh', true);
});
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();
}
}
});
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.