Alright so I am running into this issue, check it out
http://jsbin.com/rarubesuxutu/1/edit?html,css,js
I am trying to send an event right after init but the event does not get handled. Instead the action just errors out. Why does Ember handle it like this? When is it ok to send an event? Is there some callback so I can set my observers?
Thanks!
Ok so I figured it out. I had a misunderstanding of the order of the callbacks.
Here is my updated version. While it's not fixed it helps understand the order of the route and controller initializations. The IndexController init is called before the routes setupController. This triggers the event before the route was set.
Okay, so I'm not sure why you want to send an action right after init, as you should be able to define all of your variables in the setupController or in the controllers init phase, so I'll show you how I would go about it. JSBIN
Effectively what you can do is on init for the controller, you can do this:
theTruth: false,
setTheTruth: function(){
this.set('theTruth', true);
}.on('init'),
Then you can have an observer that watches for changes on this variable like so:
truthChanged:function(){
console.log("TRUTH HAS CHANGED");
}.observes('this.theTruth'),
Ideally though, you can forgo all of this by taking advantage of the routes setupController function like soJSBIN
setupController: function(controller, model) {
controller.set('model', model);
controller.set('theTruth', true);
},
This way you don't even deal with handling anything in the init event. However, say you have a requirement that makes you have to do anything with the init event, you should separate your observer from the .on('init') event, and let them coexist separately. Let me know if this works for you.
Related
I recently started learning mithril.js and I'm wondering how can I make very basic Model -> View one way data binding app.
TestModel = function(data){
this.name = m.prop(data.name)
}
testModel = new TestModel({name: "John"})
code above declare a model and it works perfectly as getter/setter.
but how can I set an event listener for the model event like Backbone's listenTo('model',"change",callbackFunc)?
all sample codes I saw are setting events for actual user actions like click,keyup or onchange.but never listen to actual model value's state directly.
am I missing something or am I understanding how to use mithril.js wrongly?
thanks in advance.
One of the key ideas with Mithril is that changes usually happens after an event:
A user action like onclick or keyup defined in a m() view template
An ajax request made with m.request
Mithril automatically redraws after those, alleviating the need for most listeners.
If you are updating your models through some other method and you need to redraw manually, use m.redraw or m.startComputation / m.endComputation. Thanks to Mithril's DOM diff algorithm, redraws are very cheap so don't be afraid to use them (with some common sense, of course!) Check out the m.redraw documentation for more info.
I have a view that represents a folder. I have bunch of subviews, that this folder view creates, each representing a unique thumbnail in that folder. It turns out that each one of those subview's render method is getting called multiple times (3). Is there a way to find out how view's render method is called. There are different places which could render a trigger event for e.g., if models metadata is changed. It has become a huge mess and I'm looking for a way to debug backbone view's to know what is exactly triggering render method.
The way that I always debug events is:
view.on('all', function(eventName){
console.log('Name of View: ' + eventName);
});
You could do this on views, models or collections.
example:
http://jsfiddle.net/CoryDanielson/phw4t/6/
I added the request and sync methods manually to simulate how backbone would actually perform. The rendered event is custom -- nothing listens to it. Just to show you how/when it happens.
So as you requested, here's an example of how to override the trigger method. Note that you'll have to override it for all types of classes (Model, View, Collection, Router).
var trigger = Backbone.Model.prototype.trigger;
Backbone.Model.prototype.trigger = Backbone.View.prototype.trigger = Backbone.Collection.prototype.trigger = Backbone.Router.prototype.trigger = function(name) {
trigger.apply(this, arguments);
console.log(this, 'triggered the event', name, '.').
}
You could be more specific by overriding each method individually to add the type of object in the log. But you got the general idea.
You might what to give backbone.debug a try. Should give you some insight into what events are being fired.
I'm trying to call view method from controller, but no idea how to do this. From view I can easily call controller method like this.get('controller').send('method');
How to do something like that from controller this.get('view').send('method');?
To give you better overview what I'm trying to do.
I have application controller Ember.Controller.extend({}) I have application view Ember.View.extend({}) and application template.
In application template is login form, when user submit it controller method is executed. In this method if login credentials are incorrect I need to call view method which is executing jQueryUI method on login form (shake method to be exact and showing some text).
This sounds like a good use for Ember.Evented. By using event subscription and dispatching you can avoid coupling your view and controller.
Simply mixin Ember.Evented:
Controller = Ember.Controller.extend(Ember.Evented)
Now you can call on and trigger methods on your controller, to subscribe to an event and then to kick off the event. So, in your view you might do:
didInsertElement: function () {
this.get('controller').on('loginDidFail', this, this.loginFail);
}
And then in your controller call this.trigger('loginDidFail') to kick off your loginFail view method.
Remember to remove the handler after the view is dismissed... see the answer below.
Just wanted to answer on this question to address the issue with properly removing the listener if the view is cleared (when the route changes). It's also not necessary to use a jquery proxy, since the on/off methods support a target, which is good because unsubscribing a proxy is definitely more complicated. Revising what Christopher provided:
didInsertElement: function()
{
this.get('controller').on('loginDidFail', this, this.loginFail);
},
willClearRender: function()
{
this.get('controller').off('loginDidFail', this, this.loginFail);
}
Without removing the subscription any subsequent visits to the login route (without reloading the page) will add additional listeners; i.e. memory leaks, errors, and unexpected behavior.
Hope any angularjs gurus can help me with this.Here is my angularjs code
$scope.$on('$routeChangeStart', function(event, next, current) {
if ($scope.myForm.$dirty) {
if(!confirm("Unsaved, do u want to continue?")) {
event.preventDefault();
}
}
});
It alerts in browser back button click when data is dirty, but on clicking cancel or ok it still completes the route change.Seems like event.preventDefault() is not working.
Can any one point out what may be wrong
I had lots of trouble finding this one, but instead of the "$routeChangeStart" event, you can listen to the "$locationChangeStart" event, for which you can prevent default:
$scope.$on("$locationChangeStart", function(event, next, current) {
if (!confirm("You have unsaved changes, continue navigating to " + next + " ?")) {
event.preventDefault();
}
});
You could also always prevent default, store "next", and display a clean JS modal and decide asynchronously.
$locationChangeStart is currently undocumented but referenced here : https://github.com/angular/angular.js/issues/2109
Fixed exactly after Angular 1.3.7
https://code.angularjs.org/1.3.7/docs/api/ngRoute/service/$route
$routeChangeStart
Broadcasted before a route change. At this point the route services starts resolving all of the dependencies needed for the route change to occur. Typically this involves fetching the view template as well as any dependencies defined in resolve route property. Once all of the dependencies are resolved $routeChangeSuccess is fired.
The route change (and the $location change that triggered it) can be prevented by calling preventDefault method of the event. See $rootScope.Scope for more details about event object.
According to the AngularJS docs (see at $broadcast) you simply cannot cancel an event of type broadcast ($routeChangeStart is of that type):
The event life cycle starts at the scope on which $broadcast was
called. All listeners listening for name event on this scope get
notified. Afterwards, the event propagates to all direct and indirect
scopes of the current scope and calls all registered listeners along
the way. The event cannot be canceled.
This problem was fixed in the newest versions ( >= 1.3.8 ).
Since the arguments supplied to $routeChangeStart are more detailed (and often more useful), if possible, try to update your angular version ...
The problem might persist if you are using a $stateProvider.
In this case use:
$scope.$on('$stateChangeStart', function( event){
.....
event.preventDefault();
});
I have an action:
{{action create target="controller"}}
which I have targeted to the bound controller (rather than the router) like this:
App.AddBoardController = Ember.Controller.extend
create: ->
App.store.createRecord App.Board, {title: #get "boardName"}
App.store.commit()
//TODO: Redirect to route
How do I redirect back to a route from the controller action?
Use transitionToRoute('route') to redirect inside an Ember controller action:
App.AddBoardController = Ember.Controller.extend({
create: function(){
...
//TODO: Redirect to route
this.transitionToRoute('route_name');
}
...
In fact, this is not Ember idiomatic. From what I know, and what I have learnt from Tom Dale himself, here are some remarks about that code:
First, you should not transitionTo from elsewhere than inside the router: by doing so, you are exposing yourself to serious issues as you don't know in which state is the router, so to keep stuff running, you will quickly have to degrade your design, and by the way the overall quality of you code, and finally the stability of your app,
Second, the action content you are showing should be located inside the router to avoid undesired context execution. The router is indeed a way to enforce a coherent behavior for the whole app, with actions being processed only in certain states. While you are putting the actions implementation into Controllers, those actions can be called at anytime, any including wrong...
Finally, Ember's controllers are not aimed to contain behavior as they rather are value-added wrappers, holding mainly computed properties. If you nevertheless want to factorize primitives, maybe the model can be a good place, or a third party context, but certainly not the Controller.
You should definitely put the action inside the router, and transitionTo accordingly.
Hope this will help.
UPDATE
First example (close to your sample)
In the appropriated route:
saveAndReturnSomewhere: function (router, event) {
var store = router.get('store'),
boardName = event.context; // you pass the (data|data container) here. In the view: {{action saveAndReturnSomewhere context="..."}}
store.createRecord(App.Board, {
title: boardName
});
store.commit();
router.transitionTo('somewhere');
}
Refactored example
I would recommend having the following routes:
show: displays an existing item,
edit: proposes to input item's fields
Into the enclosing route, following event handlers:
createItem: create a new record and transitionTo edit route, e.g
editItem: transitionTo edit route
Into the edit route, following event handlers:
saveItem: which will commit store and transitionTo show route, e.g
EDIT: Keep reading, Mike's answer discusses some of the problems with this approach.
You can just call transitionTo directly on the router. If you are using defaults this looks like App.router.transitionTo('route', context).