Handling in View vs Handling in Controller Extjs - extjs

When we working on a extjs MVC project, we can handle a view event either inside the view or controller. How do we decide what events been handled in the view and what events been handled in the controller. What is the best practice.
For example, I need to pop up a window when a button is clicked. Should the creation of the window goes to controller or resides in the container view object.

One thing you can do is utilize the control() method within your controller definition. For example, take this code,
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
},
'#editform button[action=save]': {
click: this.onClick
}
});
}
});
This is from the MVC example. You'll notice that it is using the ComponentQuery Language (http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.ComponentQuery) to select a set of Ext objects to assign the render() event. This is saying, for all panels in the viewport, call onPanelRendered() for the render() event. So in your case, you want to select some buttons. So I added another example that selects some form with an id of editform and then select the button that contains the property action = save. You can get very specific with these component queries. I would suggest reading ComponentQuery portion of the API to see further examples. This depends on how you are attempting to select the buttons.

If you are new to MVC maybe you should only listen to events in the controller, once you have developed a couple of projects and you've gained discipline and enjoyed the benefits of listening events in a controller then you would be able to mix it up (when to use a handler and when to use a controller is gonna become obvious).
E.g If you have a FormPanel and you are only gonna listen to a button's click perhaps it doesn't make much sense to create a controller just for that, but in the other hand if you have a grid with a toolbar, action buttons, itemClick's etc then it would be a good idea to have a controller for that view.
The fact that you are using MVC does not mean you are not allowed to use handlers anymore, but do it wisely.
Best regards.

Related

Extjs getComponent from another view

(Snapshoots are here for more details)
i got this : a combobox in the main view and 3 tabs that use the combobox to load their store.
The files Mathrice*.js describe the whole view and the others (in directories) are the tabs.
My question is if there is a way to get the combobox in the tabs controller
If your combobox has an ID as displayed below
Ext.create('Ext.form.ComboBox', {
id: 'mycombobox',
renderTo: Ext.getBody()
});
Then you can get the combobox component anywhere in the controller using:
var cb = Ext.getCmp("mycombobox")
//note the name is the id of the combobox
//here cb will be the combobox instance that you need.
Solution :
Just need to to understand how viewModel works in sencha (cf sencha doc)
In tab's controller, i tried to get the parent controller which contains the combobox and then i play like i'm in that view.
Below the controller of one tabs
,mathrice : this.getViewModel().getParent().getView().getController()
,init:function () {
console.log(" VPN Tab Controller");
var me=this
,selectALab = me.mathrice.lookupReference('comboboxLab')
}
If the combo and tabs are under different controllers, there's no need for the tabs controller to be aware of the combo. You want to have the combo's controller fire an event when the combo's change that you are interested in happens. The tabs controller should listen to this event and take action. This approach reduces coupling and makes your app more maintainable.

Dynamic layout using angularjs (the angularjs way)

I just recently started tinkering with AngularJs, so my question is fairly basic. For concreteness's sake, let me start with the minimal setting: a page with banner, body and footer. The goal is to make banner and footer stay at the top and bottem of the page (css position: fixed) and the body will fill in the rest.
If I were to use jQuery, I could set position: fixed for body, and listen to window's resize event to determine where I should put my body.
Presumably, I could do the same in AngularJs. But I've read many places that one shouldn't try to manipulate the layout from the code, since it's not in the philosophy of AngularJs. So, what's the best way to achieve this in the AngularJs way? Thanks.
Dynamic layout usually refers to a layout that changes depending upon the current route. Routing is how AngularJS associates a URL with a Controller and data. It becomes an important part of an app once you go beyond just 1 page.
Multiple Views
If you use something like ui-router (http://angular-ui.github.io/ui-router/) it allows you to define views that can have child views.
In your example, the header, body and footer would all be child views of a parent view. Each view would have it's own template, controller and data. When routing changes you can have just one or more of those view change, while the other views remain the same.
Binding Events
You can do all the same event bindings in AngularJS as you can in jQuery. The difference is the life-cycle of those bindings.
If you bind to the click event for a directive using the directive's element, then you don't have to worry about that binding when the directive is destroyed. AngularJS will remove the binding automatically.
When you bind to something like the window.resize event, then you have to be more careful.
link: function($scope, $el, $attrs) {
// this doesn't have to be unbind
$el.bind('click',function(e) {
// do stuff
});
// this has to be unbind on destroy
angular.element($window).bind('resize.mybinding',function(e) {
// do stuff
});
$scope.$on('$destroy',function(){
angular.element($window).unbind('resize.mybinding');
});
});
I've seen a lot of AngularJS source code bind to window.resize when it wasn't necessary. Only you know when it's needed, but sometimes you can get away with just using a watcher.
link: function($scope, $el, $attrs) {
$scope.$watchGroup(function(){
return [$window.innerWidth,$window,innerHeight];
},function(values){
// do stuff
});
});
The above does the same thing as binding. It executes a closure function when the size of the window changes, but the difference is that it's triggered only during digest and not by the window resize event. This can sometimes be more efficient for performance. As the resize is relative to only when the directive is digested.
A lot of AngularJS applications use JavaScript code to manage the layout of an application. How much JavaScript code you use, and why you do it that way is up to you, but keeping layout separated from the JavaScript side has a lot of advantages. It's better to let CSS handle it.
Bootstrap provides a nice plug-in called Affix that does just that.
<div id="banner" data-spy="affix">Fixed Banner</div>
<div>Body</div>
<div id="footer" data-spy="affix">Fixed Footer</div>
More on affix here for example:
http://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-affix.php

Pass parameter out from Ext.window.Window to an Ext.panel.Panel on close

I have a main window with a custom Ext.panel.Panel using ExtJS 5.0.1.
Clicking an item shown on the custom panel creates and shows a custom Ext.window.Window for editing said item.
For the purpose of getting some kind of answer, or a starting point for my own situation, I'll ask for standard Ext components.
On closing the custom window, a variable in the custom panel needs to be updated to show that changes have been made.
This is a different requirement to a confirmation message before closing the window; the window does not control saving of information to a DB, but needs to let another component know that changes have been made.
This should be as simple as setting a boolean unsavedChanges to true on the panel if the window's information has been altered; I've performed this on a simpler page, which did not have the additional window as an obstacle.
Due to the use of the window, my typical go-to methods of calculating a variable as use of this.up or this.lookupReference returns null, or throw an error.
I'm aware that I can consider going to the parent of the page, then try and work down, but I'm not experienced enough to consolidate standard javaScript with the up and down capabilities of ExtJS, or at least understand the intricacies of ExtJS to navigate past the "item window" back to the panel.
How would I be able to refer to the panel while closing the window in order to update the panel's data?
Well, there's a couple of ways.
First, you can pass in your panel, or a callback function, during the construction of the window. e.g.:
// in panel
var window = Ext.create('MyWindow', { callingPanel: this })
...
// in window
onClose: function() { this.callingPanel.doStuff(); }
The other way to be to register a listener to the 'close' event of the window.
// in panel
var window = Ext.create('MyWindow', {
listeners: {
scope: this,
close: this.doStuff
}
})
The listener approach tends to work best when you don't want to tightly couple the window to the calling panel.

How can I trigger two events with a "double" action?

I've got a view Foo, with an input field, and a wrapper view Bar, with a button.
When I write something in the input and then press the button, the 'change' event on Foo's model should be triggered, and then the 'click' button on Bar's, yet only the first do happen. Is there a way to trigger both, preferably at the correct order?
Thanks!
To reiterate, and confirm your question/setup. Based on your question, this is the setup you have. You want to change the Foo.model THEN trigger the click event on the Bar.
<div id="Bar">
<div id="Foo">
<input type="text">
</div>
<button id="barButton">Update</button>
</div>
It's a little bit of an odd setup. #alexanderb illustrates it the way I also think of the whole problem but I'll explain what I'd do in your scenario assuming you know something I don't about the context of the situation! :-)
If you want the change to be triggered on the Foo.model followed by the click event on the Bar button, all you need to do manually trigger the button click from your Foo view. To do this, you can do it one of several ways. The first is to directly trigger the event of your parent (Bar) view.
// When you create your Foo subview, you pass in the parent view so you can access it
var foo = new Foo({
'parent': this // this = the barView assuming you're creating FooView inside BarView
});
// In Foo after you set your model attribute, you access the parentView
this.options.parent.$el.find('#barButton').click(); // Manually trigger the click event
// or
this.options.parent.onButtonClick() // Manually run the event handler bypassing the event
This satisfies your request to have the change event followed by the button click event in the DOM context you provided. (Not sure why in this case but I can imagine some scenarios where something similar might be desired.)
The second way of doing it is to use an event aggregator pattern which allows your views to send and receive events to one another. In this scenario, what happens is your Foo view, after updating the model triggers an event that is listened to by your parent Bar view, upon which it executes some handler.
You can read more about event aggregator pattern in Backbone here:
Event Aggregator Explanation
To be honest, I really think that alexanderb's answer is the best one for this situation unless there is something more we don't know about your special setup. But, I hope you can see and compare the two and get an idea of when and why one setup might be appropriate given a particular context.
Hm, I'm a little confused. If you don't use any backbone plugis, the workflow is different.
If you press something on a screen, an event occures on view, not in model. You have to listen to that event and update the model accordingly. If you set new value in model, change model event will occur.
Say, you have a view Foo it's subview Bar and model Boo. Both views are using the same model objects.
In Bar view,
events: {
'click .button': 'onButtonClicked';
}
onButtonClicked: function () {
var value = this.$el('.input').val();
this.model.set({ someValue: value});
}
In Foo view, you can listen for model changes
initialize: function () {
this.model.on('change:someValue', this.onSomeValueChanged);
}
In this.onSomeValueChanged you can do, what ever you like.

Display selected model from sidebar in information bar

I'm working on a application which has sidebar on the right side. That displays collection. Each model of the collection has select behavior.
In the top center of the page I have one independant backbone view which acts like "info bar" and this view should update it's infomation when I select one of the views in the sidebar
How can I do it? I think that the each view in the sidebar should have "selected" event with model argument, and my question is how can I listen that change in my info bar view in Backbone.js???
This sounds like something that would be served well by an event aggregator pattern. Derick Bailey posted a really nice article on this matter which you can read here.
Event Aggregator Pattern
I extended my Backbone.View so that all views have the event aggregator object available to them like this.
Backbone.View.prototype.eventAggregator = _.extend({}, Backbone.Events);
Basically when your model view is selected, your view.eventAggregator will trigger some custom event.
sidebarView.eventAggregator.trigger('selected', this.model);
or something of this sort. With Backbone events, you can pass parameters through the trigger function to your event listener. In the above example I passed the model with the specific event.
In your main view, you'd be listening for this event.
mainView.eventAggregator.on('selected', myFunction, this);
myFunction: function(model) {
// Some code to execute - model is available through param
}
It's a pretty useful pattern. Just don't forget to unbind the event when you close out your mainView. :-)

Resources