I sent action from view to currents route controller, then to another controller, in order to write code once.
this.get('controller.controllers.study/study').send('processPersonData', data);
**DEPRECATION: Action handlers implemented directly on controllers are deprecated in favor of action handlers on an actions object ( action: processPersonData on )
at Ember.ControllerMixin.Ember.Mixin.create.deprecatedSend
What is the right way to implement this send action?
FYI: the send action works correctly.
This message indicates that the method handling the action should be under an 'actions' hash on the object, like so:
App.SomeController = Ember.ObjectController.extend({
someVariable: null,
actions: {
processPersonData: function(context) {
//implementation
},
otherAction: function(context) {
//implementation
}
}
});
It is just new semantics for action handling.
If you are trying to call an action in your controller from your view, you should use the Em.ViewTargetActionSupport mixin as follows:
App.DashboardView = Em.View.extend(
Ember.ViewTargetActionSupport, { // Mixin here
functionToTriggerAction: function() {
var data = this.get('data'); // Or wherever/whatever the data object is
this.triggerAction({
action: 'processPersonData',
actionContext: data,
target: this.get('controller') // Or wherever the action is
});
},
});
App.DashboardController = Em.ObjectController.extend(
// The actions go in a hash, as buuda mentioned
actions:
processPersonData: function(data) {
// The logic you want to do on the data object goes here
},
},
});
Related
I am using ember. I intercept one component's button click in controller. The click is to trigger a new report request. When a new report request is made, I want the newly made request to appear on the list of requests that I currently show. How do I make ember refresh the page without obvious flicker?
Here is my sendAction code:
actions: {
sendData: function () {
this.set('showLoading', true);
let data = {
startTime: date.normalizeTimestamp(this.get('startTimestamp')),
endTime: date.normalizeTimestamp(this.get('endTimestamp')),
type: constants.ENTERPRISE.REPORTING_PAYMENT_TYPE
};
api.ajaxPost(`${api.buildV3EnterpriseUrl('reports')}`, data).then(response => {
this.set('showLoading', false);
return response.report;
}).catch(error => {
this.set('showLoading', false);
if (error.status === constants.HTTP_STATUS.GATEWAY_TIMEOUT) {
this.notify.error(this.translate('reports.report_timedout'),
this.translate('reports.report_timedout_desc'));
} else {
this.send('error', error);
}
});
}
There are few think you should consider. Generaly you want to have variable that holds an array which you are render in template in loop. For example: you fetch your initial set of data in route and pass it on as model variable.
// route.js
model() { return []; }
// controller
actions: {
sendData() {
foo().then(payload => {
// important is to use pushObjects method.
// Plain push will work but wont update the template.
this.get('model').pushObjects(payload);
});
}
}
This will automatically update template and add additional items on the list.
Boilerplate for showLoading
You can easily refactor your code and use ember-concurency. Check their docs, afair there is example fitting your usecase.
I am trying to update a record in the Ember store. When I try to do this, it returns the following error:
Uncaught Error: Assertion Failed: Cannot delegate set('name', test) to the 'content' property of object proxy : its 'content' is undefined.
The controller looks like this:
import Ember from 'ember';
export default Ember.Controller.extend({
model: null,
event: {
name: "test",
id: "adfg8943224xcvsdf"
},
actions: {
editEvent (event) {
var Event = this.store.find('event', event.id);
Event.set('name', event.name);
Event.save()
}
}
});
The route looks like this:
import Ember from 'ember';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model () {
return {
event: this.store.find('event')
}
},
setupController (controller, model) {
controller.set('model', model);
}
});
The template triggers the action, sending along a object called event, which has properties like name and id. The values of the event object come from the controller and have been set before triggering the editEvent action:
<form {{action 'editEvent' event on="submit"}}>
I believe what is happening is that your model hook is returning a POJO that contains a promise that will resolve. If you want to pass that to your action then you need to do
<form {{action 'editEvent' model.event on="submit"}}>
That being said you should really just return a promise from your model hook so that Ember will wait for your data to load before rendering the template. With the way you have it setup now, if your data takes a long time to load, someone could submit the form before the model is loaded and you'll get an error.
I think you want your route to look like this (no need to override setupController):
import Ember from 'ember';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model () {
return this.store.find('event');
}
});
Then in your template:
<form {{action 'editEvent' model on="submit"}}>
If you need to load multiple models then you should use Ember.RSVP.hash.
See this answer: EmberJS: How to load multiple models on the same route?
Also, I'm not quite sure what your action is trying to do but you don't need to find the record again. The code you posted for your action doesn't actually do anything. It gets the event and then sets the event's name to its own name.
actions: {
editEvent (event) {
// you already have the event, you passed it in as a parameter
// you don't need to query the store again for it.
var Event = this.store.find('event', event.id);
// This doesn't do anything as it just sets the event.name to itself
Event.set('name', event.name);
Event.save()
}
}
I think you mean to do this:
actions: {
editEvent (event) {
event.set('name', 'updated name');
event.save();
}
}
I have a "box" route/controller as below;
export default Ember.Controller.extend({
initialized: false,
type: 'P',
status: 'done',
layouts: null,
toggleFltr: null,
gridVals: Ember.computed.alias('model.gridParas'),
gridParas: Ember.computed('myServerPars', function() {
this.set('gridVals.serverParas', this.get('myServerPars'));
this.filterCols();
if (!this.get('initialized')) {
this.toggleProperty('initialized');
} else {
Ember.run.scheduleOnce('afterRender', this, this.refreshBox);
}
return this.get('gridVals');
}),
filterCols: function()
{
this.set('gridVals.layout', this.get('layouts')[this.get('type')]);
},
myServerPars: function() {
// Code to set serverParas
return serverParas;
}.property('type', 'status', 'toggleFltr'),
refreshBox: function(){
// Code to trigger refresh grid
}
});
My route looks like;
export default Ember.Route.extend({
selectedRows: '',
selectedCount: 0,
rawResponse: {},
model: function() {
var compObj = {};
compObj.gridParas = this.get('gridParas');
return compObj;
},
activate: function() {
var self = this;
self.layouts = {};
var someData = {attr1:"I"};
var promise = this.doPost(someData, '/myService1', false); // Sync request (Is there some way I can make this work using "async")
promise.then(function(response) {
// Code to use response & set self.layouts
self.controllerFor(self.routeName).set('layouts', self.layouts);
});
},
gridParas: function() {
var self = this;
var returnObj = {};
returnObj.url = '/myService2';
returnObj.beforeLoadComplete = function(records) {
// Code to use response & set records
return records;
};
return returnObj;
}.property(),
actions: {
}
});
My template looks like
{{my-grid params=this.gridParas elementId='myGrid'}}
My doPost method looks like below;
doPost: function(postData, requestUrl, isAsync){
requestUrl = this.getURL(requestUrl);
isAsync = (isAsync == undefined) ? true : isAsync;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
// settings
}).success(resolve).error(reject);
});
return promise;
}
Given the above setup, I wanted to understand the flow/sequence of execution (i.e. for the different hooks).
I was trying to debug and it kept hopping from one class to another.
Also, 2 specific questions;
I was expecting the "activate" hook to be fired initially, but found out that is not the case. It first executes the "gridParas" hook
i.e. before the "activate" hook. Is it because of "gridParas"
specified in the template ?
When I do this.doPost() for /myService1, it has to be a "sync" request, else the flow of execution changes and I get an error.
Actually I want the code inside filterCols() controller i.e.
this.set('gridVals.layout', this.get('layouts')[this.get('type')]) to
be executed only after the response has been received from
/myService1. However, as of now, I have to use a "sync" request to do
that, otherwise with "async", the execution moves to filterCols() and
since I do not have the response yet, it throws an error.
Just to add, I am using Ember v 2.0
activate() on the route is triggered after the beforeModel, model and afterModel hooks... because those 3 hooks are considered the "validation phase" (which determines if the route will resolve at all). To be clear, this route hook has nothing to do with using gridParas in your template... it has everything to do with callling get('gridParas') within your model hook.
It is not clear to me where doPost() is connected to the rest of your code... however because it is returning a promise object you can tack on a then() which will allow you to essentially wait for the promise response and then use it in the rest of your code.
Simple Example:
this.doPost().then((theResponse) => {
this.doSomethingWith(theResponse);
});
If you can simplify your question to be more clear and concise, i may be able to provide more info
Generally at this level you should explain what you want to archive, and not just ask how it works, because I think you fight a lot against the framework!
But I take this out of your comment.
First, you don't need your doPost method! jQuerys $.ajax returns a thenable, that can be resolved to a Promise with Ember.RSVP.resolve!
Next: If you want to fetch data before actually rendering anything you should do this in the model hook!
I'm not sure if you want to fetch /service1, and then with the response you build a request to /service2, or if you can fetch both services independently and then show your data (your grid?) with the data of both services. So here are both ways:
If you can fetch both services independently do this in your routes model hook:
return Ember.RSVP.hash({
service1: Ember.RSVP.resolve($.ajax(/*your request to /service1 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
service2: Ember.RSVP.resolve($.ajax(/*your request to /service2 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
});
If you need the response of /service1 to fetch /service2 just do this in your model hook:
return Ember.RSVP.resolve($.ajax(/*/service1*/)).then(service1 => {
return Ember.RSVP.resolve($.ajax(/*/service2*/)).then(service2 => {
return {
service1,
service2
}; // this object will then be available as `model` on your controller
});
});
If this does not help you (and I really think this should fix your problems) please describe your Problem.
I am working on a site where I have to search in the DB for string that come after the / on the root domain. I can't find anything about it in the documentation.
I am trying to make it work with Iron Router but any other suggestion would work out.
Thanks for the help!
Edit: Basically I just want to pass anything that comes after domain.com/ to a variable.
Here's something i've been doing so maybe it'll lead you down the right path
Route sends URL params to ownedGroupList template
Router.route('/users/:_id/groups', {
name: 'owned.group.list',
template: 'ownedGroupList',
data: function() {
return {params: this.params};
}
});
Template ownedGroupList can access params object using this.data in onCreated, onRendered, and onDestroyed template event handlers
Template.ownedGroupList.onCreated(function(){
this.subscribe("owned-groups", this.data.params._id );
});
Template ownedGroupList can access params through this variable in helper methods
Template.ownedGroupList.helpers({
groups: function() {
return Groups.find({owner: this.params._id });
}
});
Template ownedGroupList can access params through template.data variable in event handlers
Template.ownedGroupList.events({
'click .a-button': function(event, template) {
var group = Groups.findOne({owner: template.data.params._id });
// do something with group
}
});
Here's a simple route that should do the trick
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
data: function() {
return this.params.keyword;
}
});
This will pass the keyword as the data context to your template and then you can do whatever you want with it. Alternatively you can perform the search straight in the router (especially if you're passing the keyword to a subscription so that the search runs on the server). For example:
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
waitOn: function(){
return Meteor.subscribe('keywordSearch',keyword);
},
data: function() {
return MyCollection.find();
}
});
This second pattern will send your keyword to a subscription named keywordSearch that will execute on the server. When that subscription is ready, the route's data function will run and the data context passed to your keywordTemplate will be whatever documents and fields have been made available in MyCollection.
My objective is to display a fancy "loading..." graphic on my page while Ember fetches model data through the Ember route.
This led me to http://emberjs.com/guides/routing/loading-and-error-substates/. That inspired me to create an action on my page's controller which would show the "loading" overlay window in the DOM. For example, here's my controller:
controllers/users.js:
export default Ember.ArrayController.extend({
...
actions: {
displayLoading: function() {
// Show the DOM element that says "Loading..."
},
...
}
});
I'd like to call that while my data is loading, so I then define a route as follows:
routes/users.js:
export default Ember.Route.extend({
model: function( params ) {
return this.store.find('user', params );
},
actions: {
loading: function(transition, originRoute) {
transition.send('displayLoading');
}
}
});
But when I do this, I get this error:
Uncaught Error: Nothing handled the action 'displayLoading'. 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.
So my question is where can I define this action so that my loading method will be able to call it?
Note that trying this.send('displayLoading') gave me this error:
Can't trigger action 'displayLoading' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call .send() on the Transition object passed to the model/beforeModel/afterModel hooks..
Update: I am able to catch this action on the route itself, but then I still can't call the action on my controller.
Update #2: Thanks to #kingpin2k's answer, I've resolved this. For those interested, here is a full solution:
controllers/users.js:
export default Ember.ArrayController.extend( {
actions: {
showLoading: function() {
this.set('isLoading', true);
},
hideLoading: function() {
this.set('isLoading', false);
},
}
});
routers/users.js:
export default Ember.Route.extend({
model: function( params ) {
return this.store.find('user', params );
},
actions: {
loading: function() {
this.controllerFor('users').send('showLoading');
},
didTransition: function() {
this.controllerFor('users').send('hideLoading');
}
}
});
A key insight was that I can set an isLoading property on my controller which determines whether my modal "Loading..." window is showing in the Handlebars template.
use controllerFor, http://emberjs.com/api/classes/Ember.Route.html#method_controllerFor
loading: function(transition, originRoute) {
var controller = this.controllerFor('foo');
controller.send('displayLoading');
}