In previous versions of Ember, the javacsript event object would be passed as a parameter to the event handling function of the route. In RC1 it is no longer passed. I also can not find any way to get the view object from the route. I am build a form to upload a file to the backend datastore using the FormData object. I know I could handle the event within the view rather than the route, but I would prefer to handle all events that change router state in the router itself.
This code worked in previous versions:
add_asset: Ember.Route.extend({
upload: function(router, event) {
var form = event.target.form;
var view = event.view;
var form_data = new FormData(form);
var uploadModel = new App.Asset();
var success_callback = function(){
console.log('uploaded!');
router.transitionTo('root.portfolios.show_portfolio', event.context)
};
var error_callback = function() {
console.log('error uploading');
};
uploadModel.upload(form_data, success_callback, error_callback);
}
});
I am unable to find a way to get either the event object or view object from with the route event handler. Is this possible? Thanks.
Related
Ember 2.5
I have a page that displays a list of all types of commutes (/commutetypes). To create a new commute type, I go to commutetypes/new.
Once I save the new commute type and transition back to /commutetypes. The new commute type does not display on the list. Can I refresh the model to display it?
// save action in route.
save: function() {
var _this = this;
var com = this.store.createRecord('commutetype', {
name: document.getElementById('nameInput').value,
description: document.getElementById('descriptionInput').value
});
com.save().then(function(){
_this.transitionTo('commutetypes');
});
}
Added model for commutetypes route.
model: function(params) {
return Ember.RSVP.hash({
commutetypes: this.store.query('commutetype', params)
});
},
Can you try to pass in your model inside transitionTo. Also make sure that you endpoint returns a 201 with model instance
com.save().then(function(data){
_this.transitionTo('commutetypes', data);
}
I would also need to make sure that from server side you get instance of model back to ember.
Can you post your route code commute types...maybe you are not loading data there properly
hope it helps
In the case below videoEnded function is successfully called, however how do I get a reference to the actual Ember component it self?
export default Ember.Component.extend({
videoEnded: function(){
var self = this;
alert('how do i get a reference to the actual ember component object here ?')
}
didInsertElement: function() {
var self = this;
var options = {};
self._soundjs = soundjs('soundOne', options, function(){
});
self._soundjs.on('ended',self.videoEnded);
}
The following solved my issue. videEnded callback was losing the components context. The following code resolved the issue, self passed to video ended refers to the component context which can then be used, to send actions to the context controller.
self._soundjs.on('ended',function() { self.videoEnded.apply(self, arguments); });
Credit: #teddyzeenny
I am using JSON requests to populate the model for a template called example.
App.ExampleRoute = Ember.Route.extend({
model: function() {
var url = "example/GetExamples";
return Ember.$.getJSON(url).then(function(data) {
return data;
});
}
});
The example template perfectly renders this data. Now my backend might change from time to time as I have another template called addExample. It just sends a JSON request to the server to add an entry in the database from this other template. After adding the examples, I try to transition to example template but I see old data only. On refreshing the page, the new data with the added entry comes up.
App.AddController=Ember.Controller.extend({
postAddRequest: function() {
var request = $.post("/example/AddExample", {params});
request.then(this.success.bind(this), this.failure.bind(this));
},
success: function(data) {
this.transitionToRoute('example');
}
});
So is there any way I can force Ember to re render 'example' template and invoke its model hook in the route without refreshing the page. I can't use ember-data for persistence as it is still in beta stage.
Transition to the route and send the data you'd like to use on the route. Additionally the route should be using an id/slug in the url. If not see below
this.transitionToRoute('example', data);
If you are already on the route and you want to update the model of a currently set controller you can grab that controller and set the model.
From the controller
App.SomeController = Em.Controller.extend({
needs: ['example']
someFunction: function(){
var model = ... get new model;
this.get('controllers.example').set('model', model);
}
});
The ember way:
According to ember's documentation about views' eventManagers, they must be created in the parent classes definition like so:
AView = Ember.View.extend({
eventManager: Ember.Object.create({
which encapsulates and isolates them from their parent view (AView).
The only way of accessing the context of events is through the view parameter that gets passed in along with each event
dragEnter: function(event, view) {
My situation:
I'm doing a lot of work with the various drag events inside a large view with many subviews, inputs, checkboxes, etc.
Following this form, my code is beginning to go to great lengths to determine which sub-view each event originated from, and then taking different paths to access the common parent controller:
drop: function(event, view) {
var myController;
if(view.$().hasClass('is-selected') ||
view.$().hasClass('list-map-container')) {
myController = view.get('controller.controllers.myController');
} else if(view.$().hasClass('ember-text-field')) {
myController = view.get('parentView.parentView.controller');
} else {
myController = view.get('controller');
}
// do work with myController
}
My hack:
In order to simplify I used the didInsertElement hook in the parent view to assign the desired controller as a property on the eventManager:
App.MyView = Ember.View.extend({
didInsertElement: function() {
this.set('eventManager.controller', this.get('controller'));
},
eventManager: Ember.Object.create({
controller: null,
// ...
This works to significantly simplify my event handlers:
drop: function(event, view) {
var myController = this.get('controller');
// do work with myController
My question:
My intuition tells me this hack-around isn't the best solution.
Perhaps I shouldn't be doing all the work in the eventManager? Rather move all this work to a controller and just forward the events from the view?
But if the eventManager is an acceptable workspace, then what is the best way to access the parent view's controller?
I know this is a late answer but this SO question appears as a result of google. Here is how I did this when searching through emberjs examples.
To access the view within the eventManager, you have to specify two argument in the event function handler :
eventManager: Ember.Object.create({
keyUp: function(event, view){
view = view.get('parentView'); // The view parameter might not be the current view but the emberjs internal input view.
view.get('controller'); // <-- controller
}
}),
Correct me if I'm wrong, but it looks like all the controller logic is encapsulated to a text-field--if so, I think a component might better suited for this use case. It's essentially a controller and view as one, and the eventManager's callbacks' view parameter gives you control over the component/controller itself.
If you need access to the component's parent controller, you might want to bind to events on the component from the parent controller, because the component really shouldn't know about anything outside its scope.
I am testing my application, so I am doing the following:
I show an index view (#/locators/index), of Locator objects, which I initially load with App.Locator.find();
I modify the backend manually
Manually (with a button/action) I trigger a refresh of the data in the ember frontend, without changing the route. I do this with App.Locator.find().then(function(recordArray) {recordArray.update();});. I see via console logging that a list request is sent to the backend, and that the up-to-date data is received. I assume this is used to update the store.
BUT: The view does not update itself to show this new data
Why does the view not get automatically updated when the store receives new data? Isn't that the whole point of the data binding in Ember?
If I now do the following:
Open any other route
Go back to the locators index route (#/locators/index)
Ember sends a new request to list the locators
The index view is shown, with the correct data (since it was already in the store?)
New data is received
(I am not 100% sure that 4 and 5 happen in that order, but I am quite certain)
So, my impression is that the data is properly updated in the store, but that somehow a full re-rendering of the view is needed to display this new data, for example by leaving and re-entering the route. Is this true? Can I force this re-rendering programmatically?
Ember changes view data when the underlying model is changed by the controller(Which is binded to the view)
(Only when the state of the application changes(url changes) router hooks are called)
Your problem could be solved when you do this.refesh() inside your route by capturing the action triggered by your view.
App.IndexRoute = Ember.Route.extend({
actions: {
dataChanged: function() {
this.refresh();
}
},
//rest of your code goes here
});
for this to work your handlebar template which modifies the data shoud have an action called dataChanged
example :
Assume this action is responsible for changing/modifying/deleting the underlying data
<button {{action 'dataChanged'}}> Change Data </button>
Refresh method actually does a model refresh and passes it to the corresponding controller which indeed changes the view.
There a couple of things that come to mind you could try:
If you are inside of an ArrayController force the content to be replaced with the new data:
this.replaceContent(0, recordArray.get('length'), recordArray);
Or try to call reload on every single record trough looping the recordArray:
App.Locator.find().then(function(recordArray) {
recordArray.forEach(function(index, record) {
record.reload();
}
}
And if the second approach works, you could also override the didLoad hook in your model class without having to loop over them one by one:
App.Locator = DS.Model.extend({
...
didLoad: function(){
this.reload();
}
});
If this works and you need this behaviour in more model classes consider creating a general mixin to use in more model classes:
App.AutoReloadMixin = Ember.Mixin.create({
didLoad: function() {
this._super();
this.reload();
}
});
App.Locator = DS.Model.extend(App.AutoReloadMixin, {
...
});
App.Phone = DS.Model.extend(App.AutoReloadMixin, {
...
});
Update in response to your answer
Handlebars.registerHelper is not binding aware, I'm sure this was causing your binding not to fire. You should have used Handlebars.registerBoundHelper or simply Handlebars.helper which is equivalent:
Handlebars.helper('grayOutIfUndef', function(property, txt_if_not_def) {
...
});
Hope this helps.
Somehow this seems to be due to the fact that I am using custom handlebar helpers, like the following:
Handlebars.registerHelper('grayOutIfUndef', function(property, txt_if_not_def) {
// HANDLEBARS passes a context object in txt_if_not_def if we do not give a default value
if (typeof txt_if_not_def !== 'string') { txt_if_not_def = DEFAULT_UNDEFINED_STR; }
// If property is not defined, we return the grayed out txt_if_not_def
var value = Ember.Handlebars.get(this, property);
if (!value) { value = App.grayOut(txt_if_not_def); }
return new Handlebars.SafeString(value);
});
Which I have been using like this:
{{grayOutIfUndef formattedStartnode}
Now I have moved to a view:
{{view App.NodeIconView nodeIdBinding="outputs.startnode"}}
Which is implemented like this:
App.NodeIconView = Ember.View.extend({
render: function(buffer) {
var nodeId = this.get('nodeId'), node, html;
if (nodeId) {
node = App.getNode(nodeId);
}
if (node) {
html = App.formattedLabel.call(node, true);
} else {
html = App.grayOut(UNDEFINED_NODE_NAME);
}
return buffer.push(html);
}
});
I am not sure why, but it seems the use of the custom handlebars helper breaks the property binding mechanism (maybe my implementation was wrong)