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.
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 use an observable inside the updateDocumentTitle function that gets a new value after completing an ajax request. But I noticed that the updateDocumentTitle function doesn't fire again after the observable changes its value.
userShell.prototype.router.updateDocumentTitle = function(instance, instruction) {
document.title = instance.userArr().name();
};
I tried wrapping the contents of updateDocumentTitle inside a computed observable, but for some reason when I navigate between user pages that are under the same shell (users/100 to users/105), the computed observable gets called as many times as I have navigated between pages without refreshing.
Are there any other successful ways of setting a dynamic document title?
It's a competition between when updateDocumentTitle() fires and when your AJAX request completes successfully.
Where does your AJAX call get made, in which Durandal handler for the viewModel? You have activate, attached, and compositionComplete handlers to choose from.
[EDIT]
You will need to abandon updateDocumentTitle() in this case. Simply create an observable in your viewModel. Update that observable from the activate handler upon successful completion of your AJAX call. Make sure you bind to that observable in your view. We actually do that ourselves for the same reasons you would need to.
For robustness, make sure you provide a default title in the event your AJAX call fails.
Using $routeProvider every time user clicks on a link, a new $scope is being generated. That means all the data is lost. How can i make Angular use the same controller/$scope?
Explanation:
http://jsfiddle.net/mpKBh/1/
(click on links)
<a href='#'>First controller</a>
<a href='#/view'>Second controller</a>
$routeProvider.
when('/', { template:"{{$id}}",controller: ContentListCtrl}).
when('/view', {template:"{{$id}}",controller: ContentDetailCtrl}).
P.s. is it possible to know which controller is currently active?
In AngularJS, $scope is not meant to hold data that persists across your application. For that, you want to use a service that is injected into both controllers. If you provide more detail on what data you're missing across routes, I would be happy to revise this answer to include something a little more actionable.
In re your PS: You can inject the $route service to get information about the current route; the $route.current.controller property will give you the constructor function of the current route.
For those researching how to "unbind" in AngularJS, he is a bit of info (related to the OP's last comment above)
When a view is destroyed, it's basically marked for garbage collection - but it's still there. That is why you are getting multiple requests when a scroll happens - because it's still listening for events.
So the easiest way to deal with this (that I have found, though I'd like to learn of other ways) is to listen for the $destroy event and react on it.
You can "unbind/unlisten" for an event by keeping a reference to what is returned by the $on method. Here is an example taken from a controller:
$scope.systemListener = $rootScope.$on("someEventYouListenTo", function (event, data) {
console.log('Event received by ' + $scope.name);
});
$scope.$on('$destroy', function () {
// Remove the listener
$scope.systemListener();
});
Now those old scopes/views won't react to events anymore.
Hope that helps someone!
Perhaps this seems a bit backwards, but I have a view bound with Rivets.js for which I'd like the view to populate the model on initialization.
The usecase is that I'm using server-side rendering to return a snippet (the view) including rivets' data-attributes. So NO JSON is returned from server to client.
Now, by pressing 'edit' a user may put the content in 'edit'-mode, and start editing at will. (Using contenteditable, but this is out of scope here I guess).
So how to make sure the model is populated with values from the view on init?
I know that this question is a little outdated but I recentry tried rivets and I came across the same problem.
The solution:
// In your rivets configuration you disable preload:
rivets.configure({
templateDelimiters: ['[[', ']]'],
preloadData: false
});
// you bind your data
var binding = rivets.bind($('#auction'), {auction: auction});
// you manually publish it once to populate your model with form's data
binding.publish();
And that's it. I still don't know how to disable prelaod per bind
From the example on Rivets website (assign to 'rivetBinding')
var view = rivets.bind($('#auction'), {auction: auction});
doing rivetBinding.publish(); will bootstrap the model with values from the view for all bindings that have 'publishes = true'.
This question is old but it still has no accepted answer, so here goes:
You need to disable the preload configuration so rivets doesn't override whatever is in the input with what you have in your model at the time you do the binding. This can be done via the preloadData=false configuration, either globally (rivets.configure(...)) or view-scoped (third param to rivets.bind(...)).
After the binding, you need to publish the view (pull the values to your model). You also need to set up the observers via sync() call, otherwise your binded methods won't be triggered.
Using the same example as the previous answers:
var view = rivets.bind($('#auction'), { auction: auction }, {
preloadData: false
});
view.publish();
view.sync();
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).