Using tryInvoke when controller actions are wrapped - ember.js

Newest guidelines state to wrap controller actions inside a
actions: {
loadMore: function() {}
}
When I do so the code I previously had to fire an action on a controller no longer works:
Ember.tryInvoke(view.get('controller'), 'loadMore');
What would be the proper way to get this going again?
Edit
The complete code that uses the tryInvoke:
didInsertElement: function() {
'use strict';
var view = this;
this.$().bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) {
Ember.tryInvoke(view.get('controller'), 'loadMore');
}
});
},

You can use the Ember.ViewTargetActionSupport, and set the action property to the action name that you want, in that case loadMore. Using triggerAction will send the action.
Because the jquery event is detached from ember run loop I wrapped the triggerAction in Ember.run.
App.YourView = Ember.View.extend(Ember.ViewTargetActionSupport, {
action: 'loadMore',
didInsertElement: function() {
'use strict';
var view = this;
this.$().bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) {
Ember.run(function() {
view.triggerAction();
});
}
});
},
});

Are you sure you're getting the controller right? According to a commit a couple months ago in Ember 1.2, the controller is now accessible from a view as "targetObject" rather than "controller":
https://github.com/emberjs/ember.js/commit/326af5a9c88df76f5effe11156a07b64c8b178a3#packages/ember-handlebars/lib/controls/text_support.js

Related

Schedule for every afterRender for a View

I would like to run code every time a view is rendered. The closest I can get is to listen to every property that could change and explicitly schedule something on the run loop for afterRender, but I would love to just have a lifecycle hook like afterRender. The property approach gets fragile, since you have to keep the list of properties up to date based on what can affect the render.
Controller:
App.IndexController = Ember.Controller.extend({
count: 0,
actions: {
add: function() {
var count = this.get('count');
count += 1;
this.set('count', count);
}
}
});
View:
App.IndexView = Ember.View.extend({
changed: function() {
Ember.run.scheduleOnce('afterRender', this.after);
}.observes('controller.count'),
after: function() {
console.log('after render', this.$('span').text());
}
});
Template:
<button {{action "add"}}>add</button> <span>{{count}}</span>
http://emberjs.jsbin.com/wujogejeso/3/edit?html,css,js,output
To schedule the code afterRender, you can use the didInsertElement lifecycle hook in place of your changed function.
App.IndexView = Ember.View.extend({
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this.after);
},
after: function() {
console.log('after render', this.$('span').text());
}
});

Ember template doesn't update after saving model with EmberFire adapter for Firebase

I'm using Firebase as the backend for my Ember app and successfully hooked up Firebase with EmberFire. However, when I try to save my models after creating a record, while they are posted to Firebase, they are not updated in the client's ember template.
My code for the action handler before I realized this was:
actions: {
publishPost: function() {
var newPost = this.store.createRecord('post', {
title: this.get('post.title'),
body: this.get('post.body'),
timestamp: new Date()
});
newPost.save();
}
}
And my code to try and solve this (but that doesn't work) by catching the promise returned by the save() and find() methods is:
actions: {
publishPost: function() {
var newPost = this.store.createRecord('post', {
title: this.get('post.title'),
body: this.get('post.body'),
timestamp: new Date()
});
this.store.find('post').then(function(posts) {
posts.addObject(newPost);
posts.save().then(function() {
newPost.save();
});
});
}
}
Am I just not handling the logic in the above code correctly to update the template or is there another Firebase/EmberFire compatible solution for this?
On https://github.com/broerse/ember-cli-blog-fire I did this:
import Ember from "ember";
export default Ember.ObjectController.extend({
actions: {
createPost: function() {
var newPost = this.get('store').createRecord('post');
newPost.set('date' , new Date());
newPost.set('author' , 'C.L.I. Ember');
this.get('target').transitionTo('post', newPost.save());
}
}
});
transitionTo can handle the newPost.save() promise.
Try it on: http://exmer.com/bloggrfire/posts

integrate hammerJS with emberJS

I'm trying to integrate HammerJS v2.0.2 with EmberJS 1.6.1.
I used many code samples but non of them work, I get different errors. lets take the simplest one.
I implemented this view:
ListApp.ScrollerView = Ember.View.extend({
setupTap: function() {
var self = this;
this.hammer = new Hammer(this.get('element'));
console.log(this.get('element'));
var tap = new Hammer.Tap();
tap.set('enable', true);
this.hammer.add(tap);
this.hammer.on('tap', function() {
console.log('before tap!');
self.tap();
});
}.on('didInsertElement'),
tap: function() {
console.log('tap!');
}
});
but I get the following error after the page is loaded:
Uncaught TypeError: Object.keys called on non-object
any idea why I see this?
Here is working bin with hammer integrated to a view.
Here's the relevant code on how you integrate hammer into a view
App.Hammer = Em.View.extend({
templateName: 'hammer',
setupTap: function() {
Hammer(this.$()[0]).on("tap", this.tap.bind(this));
}.on('didInsertElement'),
tap: function() {
alert('My tamplate name is ' + this.get('templateName'));
}
});

Ember - How can I call the route's action from my controller

I'm using ember-simple-auth along with ember-validations to validate my user login credentials
in order to validate I "override" the login route's login action in controller.
The problem is that after validation I now wanna bubble up the action; however, since validate returns a promise I can't just simply return true.
I tried calling my route with this.get('target').send('login') but apparently it doesn't work.
I tried this.send('login') but this creates an infinite loop as the controller calls itself recursively.
Just use a different action name in the controller and call login there
actions: {
validate: function() {
var that = this;
return this.validate().then(function() {
that.send('login');
}, function() {
// report errors in an array
var errors = that.get('errors');
var fullErrors = [];
Object.keys(errors).forEach(function(val) {
if(errors[val] instanceof Array)
errors[val].forEach(function(msg) {
fullErrors.push([val, msg].join(" "));
});
});
that.set('fullErrors',fullErrors);
});
},
loginFailed: function(xhr) {
this.set('errorMessage', xhr.responseText);
}
}

How do I call an action method on Controller from the outside, with the same behavior by clicking {{action}}

Please look at this code...
```
App.BooksRoute = Ember.Route.extend({
model: return function () {
return this.store.find('books');
}
});
App.BooksController = Ember.ArrayController.extend({
actions: {
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
}
});
```
I want to call the updateData action on BooksController from the outside.
I tried this code.
App.__container__.lookup("controller:books").send('updateData');
It works actually. But, in the updateData action, the this is different from the one in which updateData was called by clicking {{action 'updateData'}} on books template.
In the case of clicking {{action 'updateData'}}, the this.filter() method in updateData action will return books models.
But, In the case of calling App.__container__.lookup("controller:books").send('updateData');, the this.filter() method in updateData action will return nothing.
How do I call the updateData action on BooksController from the outside, with the same behavior by clicking {{action 'updateData'}}.
I would appreciate knowing about it.
(I'm using Ember.js 1.0.0)
You can use either bind or jQuery.proxy. bind is provided in JS since version 1.8.5, so it's pretty safe to use unless you need to support very old browsers. http://kangax.github.io/es5-compat-table/
Either way, you're basically manually scoping the this object.
So, if you have this IndexController, and you wanted to trigger raiseAlert from outside the app.
App.IndexController = Ember.ArrayController.extend({
testValue : "fooBar!",
actions : {
raiseAlert : function(source){
alert( source + " " + this.get('testValue') );
}
}
});
With bind :
function externalAlertBind(){
var controller = App.__container__.lookup("controller:index");
var boundSend = controller.send.bind(controller);
boundSend('raiseAlert','External Bind');
}
With jQuery.proxy
function externalAlertProxy(){
var controller = App.__container__.lookup("controller:index");
var proxySend = jQuery.proxy(controller.send,controller);
proxySend('raiseAlert','External Proxy');
}
Interestingly this seems to be OK without using either bind or proxy in this JSBin.
function externalAlert(){
var controller = App.__container__.lookup("controller:index");
controller.send('raiseAlert','External');
}
Here's a JSBin showing all of these: http://jsbin.com/ucanam/1080/edit
[UPDATE] : Another JSBin that calls filter in the action : http://jsbin.com/ucanam/1082/edit
[UPDATE 2] : I got things to work by looking up "controller:booksIndex" instead of "controller:books-index".
Here's a JSBin : http://jsbin.com/ICaMimo/1/edit
And the way to see it work (since the routes are weird) : http://jsbin.com/ICaMimo/1#/index
This solved my similar issue
Read more about action boubling here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
SpeedMind.ApplicationRoute = Ember.Route.extend({
actions: {
// This makes sure that all calls to the {{action 'goBack'}}
// in the end is run by the application-controllers implementation
// using the boubling action system. (controller->route->parentroutes)
goBack: function() {
this.controllerFor('application').send('goBack');
}
},
};
SpeedMind.ApplicationController = Ember.Controller.extend({
actions: {
goBack: function(){
console.log("This is the real goBack method definition!");
}
},
});
You could just have the ember action call your method rather than handling it inside of the action itself.
App.BooksController = Ember.ArrayController.extend({
actions: {
fireUpdateData: function(){
App.BooksController.updateData();
}
},
// This is outside of the action
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
});
Now whenever you want to call updateData(), just use
App.BooksController.updateData();
Or in the case of a handlebars file
{{action "fireUpdateData"}}