In my app using Emberjs, every action gets triggered 3 times on a single click.
For example with the following template and view:
Template:
<button {{action "removeFoo"}}>remove</button>
View with click handler:
listsView = Ember.View.create({
templateName: 'lists',
removeFoo: function(event) {
event.preventDefault();
console.log(new Date().valueOf());
}
})
I get the following 3 outputs in the console:
1333634360209
1333634360215
1333634360217
Does anybody know what's causing this or what's the best approach to debug the problem?
The actual problem was, that the Ember app was part of a Rails application which had two other Ember apps already. Those Ember apps didn't have a rootElement specified. Adding a rootElement for every Ember app fixed the problem.
I'm not sure why it's being called multiple times, but are you intentionally overriding Ember.View#remove? If so you'll probably want to call this._super() so that it destroys the element etc...
Here's the definition of it in the source:
https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/view.js#L770
If that wasn't your intention, you might want to call your action something else and see if resolves the problem.
Using the latest Ember.js version 0.9.6 works fine, see http://jsfiddle.net/pangratz666/BxccU/
Related
I'm trying to implement an action on a controller and get the warning:
DEPRECATION: Action handlers implemented directly on controllers are deprecated in favor of action handlers on an actions object
If I use Em.ObjectController.create(), when I click my button I get a warning stating that actions must be provided at extend time. However, if I use Em.ObjectController.extend(), when I click the button I get an error saying the action did not exist on the controller.
I created a jsBin to view this
//App.ToolbarController = Ember.ObjectController.create({
App.ToolbarController = Ember.ObjectController.extend({
model: { fu: "baar" },
actions: {
doSomethingUseful: function(data) {
console.log("doing nothing useful...");
}
}
});
I took a look at your jsBin
First off in the future its easier to debug your templates if you post them as an embedded script tag instead of the compiled handlebars function.
I've made a bin that fixes your issue.
I believe your issue had to do with the method you were using to create your view.
{{view App.ToolbarView controllerBinding="App.ToolbarController"}}
I'm not sure if that works properly.
Instead you should use the render helper
{{ render 'toolbar' }}
That way ember will try to find the toolbar view, controller and template and wire them all together properly.
To explain my exact use-case:
So I have a news-model with a 'created_at' field. Displaying all the dates with an ArrayController works fine and as expected. But what I want to do now, is converting these dates into fuzzy times with http://pragmaticly.github.io/smart-time-ago/ (or http://timeago.yarp.com/).
The problem here is, I need to call $('.timeago').timeago('refresh'); once the timestamps are loaded and displayed. That seems to work fine as long as I navigate within the ember-app. But when I refresh a site the plugin somehow can't convert it.
So I suppose the command doesn't get called at the right time from the app.
I am doing that in the following two ways right now:
in the View:
didInsertElement: function(){
$('body').timeago('refresh');
}
and in the Controller:
updateFuzzyTime: function(){
$('body').timeago('refresh');
}.observes('content')
When I do it in either place with the setTimeout() command set to 1000ms it obviously does work.
So how can I get ember to perform that command when a model is fully loaded and displayed on a refresh?
(In another project I had a similar issue and there I used the rerender() command which did work, but in this use-case I simply can not do that)
Edit: Just to post it here as well. The plugin looks at the datetime-attribute of a time element. So my hbs code looks like this:
<time class="timeago" {{bindAttr datetime="date"}}>{{date}}</time>
And I suppose the reason why most hooks don't work is because even though the DOM is rendered I suppose Ember hasn't updated the attribute yet.
Try calling it in an afterRender callback. That usually executes after everything else.
init: function() {
this._super();
Ember.run.scheduleOnce('afterRender', this, 'updateFuzzyTime');
}
updateFuzzyTime: function() {
$('body').timeago('refresh');
}
I guess I'm missing something really basic, but the following fails with The action 'login' did not exist on App.AppController.
Template:
<h2>Click the button and watch the console</h2>
<button type="button" {{action login target="App.AppController"}}>Log in</button>
Controller:
App.AppController = Ember.Controller.extend({
login: function() {
// never gets here
debugger;
},
send: function() {
// or even here
debugger;
}
});
JSBin: http://jsbin.com/okuqiv/5/edit (this is pointing to Ember latest, but it's the same behavior with 1.0-rc2, the version I've been using for a couple of days; I haven't tried it on previous versions).
Upon debugging, it seems that somehow the controller mixin is not exposing all the functions it should -- login() is a function I added, but send() is an Ember function. It's also missing other functions like get().
I saw that in my app the login() function exists on the object App.AppController.prototype, while in the JSBin it exists on one of the objects in the mixin chain.
At this point I'd even be happy to handle the login action in the view, or the router (as it seems it was the default in the past), but none of those seem to work.
From the documentation, the current default place where the actions handlers should live is the controller anyway, and then the routes, but if I remove the target and add login() to the route, I get another error: Nothing handled the event 'login' (I'm using the target in the first place because in my app /login is a separate route and has a different controller).
Solutions and workaround will be very appreciated!
You are targeting the class App.AppController and not an actual instance of that class. If you are using the router and are in an app view then action would target the appController by default.
I've updated the JSbin to call login on the IndexController since your example is in the index route:
http://jsbin.com/okuqiv/10/
You were also over riding the send method which was preventing login from being called.
I don't know, if you have seen this demo-app yet: http://www.johnpapa.net/hottowel/ but once you start it, you see a really nice loading screen at the beginning like you would in any bigger desktop application/game.
So I haven't had the chance to go through the code properly myself, but I have started recently with Emberjs and I have the feeling that loading all the js-code for the whole SPA that I am building could be in the seconds area.
My question now, how would such a loading screen be possible with emberjs?
Or would there be a better way to go about that? (I somehow don't think requirejs would be a solution, though I could be wrong)
I'd like to contribute an alternate answer to this. By default, ready fires when the DOM is ready, and it may take some time to render your application after that, resulting in (possibly) a few seconds of blank page. For my application, using didInsertElement on ApplicationView was the best solution.
Example:
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
$("#loading").remove();
}
});
Please note that Ember also offers the ability to defer application readiness, see the code for more information.
Maybe it's my lazy way of doing things, but I just solved this by adding a no-ember class to my div.loading and in my CSS I added
.ember-application .no-ember {
display: none;
}
(Ember automatically adds the ember-application to the body.)
This way, you could also add CSS3 animations to transition away from the loading screen.
you can do something like this:
App = Ember.Application.create({
ready: function () {
$("#loader").remove();
}
});
in your body you set something like this
<img src="img/loading.gif" id="loader">
Alternative to using didInsertElement, the willInsertElement is a better event to perform the loading div removal since it will be removed from the body tag "before" the application template is rendered inside it and eliminates the "flicker" effect ( unless using absolute positioning of the loading div ).
Example:
App.ApplicationView = Ember.View.extend({
willInsertElement: function() {
$("#loading").remove();
}
});
Ember has an automagic loading view logic.
By simply setting App.LoadingView and its template, Ember will show that view while application loads.
This feature is likely to change in next release, in favor of a nested loading route feature which looks promising. See below:
Draft documentation
Feature proposal and discussion
In Ember 2.0 there is no more View layer, but you can do the same with initializers:
App.initializer({
name: 'splash-screen-remover',
initialize: function(application) {
$('#loading').remove();
},
});
General Problem
Problems can arise, when one trys to integrate DOM Manipulating 3rd Party libraries. In this particular case, a jQuery Lightbox plugin caused problems.
Original Question
Currently i am trying to integrate the following Lightboxplugin into an Ember View: http://codecanyon.net/item/jquery-lightbox-evolution/115655
This is my current code for the View:
App.LightboxView = Ember.View.extend({
templateName : 'whatever',
isVisible : false,
willInsertElement : function(){
var that = this;
$.lightbox(this.$(),{
width : 600,
height : 850,
onOpen : function(){
that.$().fadeIn(400);
that.set("isVisible", true);
},
});
},
});
This approach is working quite fine as it is starting the plugin in the moment the View is inserted into the DOM and everything is displayed fine. Ember seems to insert the resulting html of the View into the DOM and the lightbox plugin fetches the DOM Element and places it into the place where it is needed for the lightbox to work.
But this is where the problems start. Actions do not work anymore in my view. I have some {{action}} helpers in my view and none of them works (they are working when i don't use the lightbox). I assume this is because the plugin makes manipulations to the DOM, but Ember wants to be the only one to manipulate the DOM.
Can anyone offer some guidance here? This is the first time i am developing/utilizing a lightbox, so this could cause additional problems :-)
This is the solution i found with help of sly7_7:
What caused the problem?
As outlined in my question i wrote a new View that should get displayed as a Lightbox with the the help opf a Lightbox plugin. That meant the DOMElement hat was inserted by EmberJS would be moved inside of the DOM by the plugin. The problem was that my Application was declared this way:
App = Ember.Application.create({
rootElement : "#ember-application-root",
});
And the corresponding HTML was:
<html>
<body>
<div id="ember-application-root" />
</body>
</html>
The root of the application just referred to a part of the DOM. The Lightbox plugin inserted its content at the end of the DOM, which meant that the Ember View was pulled outside of div for the Ember App and therefore lost its event listeners.
The solution:
Make the body element the root of the Ember Application. As sly7_7 outlined in his comment, this is the default case for the Ember Application.
App = Ember.Application.create({
// rootElement : "#ember-application-root", - defaults to body now
});