I have two ViewController.
The first one fires an event if I select an item in a treepanel :
// treeController
onItemSelection: function(treeview, record) {
var me = this,
controller = me.getView().getController();
...
controller.fireEvent('myEvent', record);
}
The second one is listening to this event.
The controller is responsible for uploading a file to a specified url.
This url is set by the onMyEvent-function.
// uploadController
...
listen: {
controller: {
'*': {
myEvent: 'onMyEvent'
}
}
},
defaultUrl: 'foo/bar/{id}',
currentUrl: null,
onMyEvent: function(record) {
var me = this;
me.currentUrl = me.defaultUrl.replace('{id}', record.getId());
},
onUploadClick: function (form) {
var me = this;
if (form.isValid()) {
form.submit({
url: me.currentUrl,
...
});
}
},
...
What I want to achieve:
I select an item in the treepanel
-> the event is fired, the onMyEvent-function has been executed.
I click on the button to open the uploadView (the view which is connected to the controller). After that I'll click on the fileupload-button, select a file and click on the uploadbutton.
After the uploadbutton has been pressed, the controller should call the onUploadClick-function and use the previous placed url (currentUrl) for the upload.
The problems I'm facing:
Selecting an item in the treepanel fires the event, but the uploadController is not executing the onMyEvent-function.
When I open the uploadView first and select afterwards a node in the panel, the onMyEvent-function is executed.
When I use the second approach and try to upload the file, I get an error which tells me I haven't specifed the url (its null).
How can I accomplish the process without using the mentioned workaround for 1.?
Thanks in advance.
Your event myEvent is in UploadController. However, controller.fireEvent('myEvent', record); would try to find and fire it in TreeController.
Root cause of this issue is that controller = me.getView().getController(); is going to give you back this/instance of TreeController. When you do me.getView(),it gives you TreeView and me.getView().getController() is going to give you back an instance of TreeController and you need an instance of UploadController cause myEvent is an event of UploadController.
The reason you're able to fire the event when you open UploadView first is cause you're already in UploadController.
Hope that helps!
Related
I am using the admin-dashboard template and made the following modification to the MainController:
routes: {
':node': {
before : 'checkSession',
action : 'onRouteChange'
}
},
Thus, when I change a route the before route will happen first, so the following method will first get called and then the routing will proceed onward:
checkSession : function() {
var args = Ext.Array.slice(arguments),
action = args[args.length - 1],
hash = window.location.hash;
// TODO: use hash to clear patient header when appropriate
if (hash.indexOf('#new-patient') > -1) {
console.log(hash);
}
action.resume();
},
This works splendidly. However, when I get the condition of hash.indexOf('#new-patient') > -1 I would like to clear a comboBox that I have defined in a view as:
bind: {
store: '{patients}'
},
reference: 'patientCombo',
This is from a view in the same scope as the above method from the viewController is, that is they are both in the hierarchy of the same viewModel. I am just not sure exactly how I can clear this from the above method. I just want to set the combo box back to no selected value (if one had been selected) and then update the bindings. Merci!
When I tried using Ext.ComponentQuery, I initially referenced the itemId, but the xtype was wrong (the combobox and displaytext, among other sundries are in a container). Since this is the only combobox in my app now, I just tried it as:
combo = Ext.ComponentQuery.query('combobox')[0]
if (combo){
combo.setValue('')
}
and, lo' and behold, it worked!
I'm implementing a simple (at least ,that was the goal) Kendo UI grid that displays two columns: one holding a checkbox, bound to a boolean, and one holding a display name for the item. The checkbox column has a simple template, and the change() event of the checkbox is handled so that the model in the datasource gets updated. I have verified this, and it works.
The data source has been configured for batch, and defines a transport for read and update. Both call a function that perform the ajax call. As I said before, the read function is handled as expected. However, the update function defined on the transport is not. The sync() on the datasource is triggered with a simple button whose click event is hooked to a function that calls datasource.sync() (or grid.saveChanges()).
transport: {
read: function(options) {
return loadStuff(options);
},
update: function (options) {
return updateStuff(options);
}
}
When debugging in the Kendo UI code, it looks like the models attribute on the ModelSet is always empty, and therefore the sync() decides that there's nothing to sync. Anyone got a clue what is happening here?
UPDATE:
Looks like something may be wrong when handling the checkbox check / uncheck. Apparently I should use something like
$('#divGrid').on('click', '.chkbx', function() {
var checked = $(this).is(':checked');
var grid = $('#divGrid').data().kendoGrid;
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set("Selected", checked);
});
Unfortunately, it looks like the set() method is not defined on the data item. When debugging, it only contains the data, and no Model object having the set() method.
UPDATE 2:
Tried wrapping the data returned from the ajax call in a model defined with Model.define(). That seems to solve the issue of the model not being dirty, as the _modified property on the model returns true. However, the models array in the ModelSet remains empty. Is this a bug in Kendo UI, or am I going the wrong way?
You don't actually need to bind to click event on the checkboxes.
I´ve posted an example on using it in JSFiddle where you can see it running. This example displays in a grid two columns: first text (tick) and second boolean rendered as a checkbox (selected); the update is batch (so, it's pretty close to what you have).
Questions to keep in mind are:
For displaying the checkbox while not in edit mode, you should define a template, something like this. You might realize that the checkbox is in disabled state by default since you want to edit it as other fields (selecting the cell first). This also guarantees that the model is correctly updated:
{
field : "selected",
title : "Selected",
template: "<input type='checkbox' name='selected' #= selected ? 'checked' : '' # disabled/>"
}
Define in the model that this field is boolean:
schema : {
id : "id",
model: {
fields: {
symbol : { type: "string" },
selected: { type: "boolean" }
}
}
},
Define the transport.update function, something like:
transport: {
read : function (operation) {
// Your function for reading
},
update: function (operation) {
// Display modified data in an alert
alert("update" + JSON.stringify(operation.data.models, null, 4));
// Invoke updating function
// that should ends with an operation.success(the_new_data)
// In this example just say ok
operation.success(operation.data.models)
}
}
EDIT: If you want to be able to modify the checkbox state without having to enter in edit mode first, you should:
Remove the disabled from the template:
{
field : "selected",
title : "Selected",
template : "<input type='checkbox' name='selected' #= selected ? 'checked' : '' #/>"
},
Then bind the click event on checkboxes to the following handler function:
$("#stocks_tbl").on("click", "input:checkbox", function(ev) {
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set("selected", this.checked);
});
Where #stocks_tbl is the id of the div that contains the grid. You might see it running here.
NOTE: It's important the on with the three parameters for making it live
I have a simple ExtJS application that is written MVC style (much of my information from here).
My application creates a viewport, and has a view with a form, some fields and a button.
The application has a controller with a 'loginButtonClick" function, and the controller watches the click event with a:
this.control({
'loginwindow button[action=save]': {
click: this.loginButtonClick
}
}
That works great, but now when the login window is showing, I want the enter key to also execute the loginButtonClick method.
I have tried all kinds of things, but the basic issue I am having is WHERE to put the code for creating the keymap, and how to bind it to the proper instances.
For example, if I create the keymap in the controller (which is my preference), I need to get the specific view instance for that controller (I might have multiple windows open of the same kind).
So, How would you create a key map (or?) from within a controller for it's view (window), calling a local method (this.loginButtonClick) when the enter key is pressed?
What you can do is bind the code that initializes the keyMap to the login window afterrender event like this:
this.control{(
'loginwindow' : {
afterrender: this.initializeKeyMap
}
Then make a function that sets up the keyNav:
initializeKeyMap: function(window, options) {
this.keyNav = Ext.create('Ext.util.KeyNav', window.el, {
enter: this.loginButtonClick,
scope: this
});
}
Now when the dialog is loaded if the user presses the Enter key, it should execute your function.
You could setup all these things on your window render event. So when it is rendered you add an eventlistener for the enter key, and in the handler you call programatically click on the login button.
You can achieve this by adding the following listeners to your text/password fields in the form
listeners: {
specialkey: function(field, e){
if (e.getKey() == e.ENTER) {
var form = this.up('form').getForm();
submitLoginForm(form);
}
}
}
Here 'form' is your form and 'submitLoginForm' is the function called at form submit which I guess is loginButtonClick in your case.
Hope this helps.
I'm creating a dynamic controller, according to the new MVC pattern in ExtJS4 and ran into a small problem. I've used the this.control method to attach the controller to my view. When create the controller a second time (going back and forth in my navigation), I have attached them couple times. My question is : What is the best way to destroy a controller or to remove all the listeners that I've setup via the this.control command.
Thanks in advance
Chris
The code of my new controller looks like like :
I create the new controller like this:
var step1Controller = Ext.create("MyApp.controller.Step1Controller", {
application : this.application
});
step1Controller.init();
In in the init function of my controller I've attached my controller to the view like this:
init : function() {
this.addEvents(['step1completed','basecontructionaborted']);
this.setupScreenLayout();
this.getTmpConfiguredControlModelsStore().removeAll();
this.application.fireEvent("addBreadCrumb", "Inbetriebnahme");
this.application.fireEvent("addBreadCrumb", "Schritt 1/3");
this.control({
'#addmodelbutton' : {
click : this.onAddBtnClick
},
'#modelviewer' : {
modelselected : this.onPanelSelect
},
'#navigationcontainer #movemodelleftbutton' : {
click : this.onMoveModelLeftClick
},
'#navigationcontainer #continuestep2' : {
click : this.onContinueStep2Click
},
'#navigationcontainer #abortbutton' : {
click : this.onAbortButtonClick
}
});
console.log('[BaseConstruction | init] completed');
}
Old question, but I killed half a day on solving this, so I'll post how I was able to get around it. This question seems very similar to my own issue. Hope it's useful to someone else.
I am loading controllers/views dynamically, and all listeners were attached via the app.control inside of a controller's init(). Worked fine until I started destroying/initializing views repeatedly. The listeners remained on the views after view.destroy(), so initializing them later down the road caused those listeners (ie render, click, etc) to fire twice.
This solved it for me:
app.control({
'element': {
beforerender: {
fn: function(thing){
// beforerender stuff for thing
thing.on('select', function(this, record, item, index){
console.log('select fired');
});
},
single: true
},
}
});
Note the "single: true" that's attached to the 'beforerender'. That's the crucial part. All other listeners that were previously written like 'beforerender' were moved to inside of it with the .on().
Cheers!
I'm fiddling with a view and related model that look like that:
App.Views.Addresses = App.Views.Addresses || {};
App.Views.Addresses.Address = Backbone.View.extend({
events: {
"click button#foo" : "clear"
},
initialize: function(model){
this.address = model.model;
this.address.view = this;
_.extend(this, Backbone.Events);
this.render();
},
render: function(){
... rendering stuff
},
clear: function(){
this.address.clear();
}
});
and
var Address = Backbone.Model.extend({
url: function() {
... url stuff
},
clear: function(){
this.destroy();
this.view.remove();
}
});
I'm facing two problems here. The first one:
I have a button with id="foo" in my source and would like the view to catch the 'click' event of this very button and fire the 'clear' event. Problem: This does not work.
Anyway calling 'clear' on my model by hand cleanly removes the data on the server but does not remove the view itself. Thats the second problem. Hopefully someone more experienced can enlighten me.
Thx in advance
Felix
First problem:
Your button must be inside the element rendered by the view.
backbone scope events to inner elements only
You must render your view within this.el element
backbone use that element for delegation
Second problem:
Use events to destroy your view
You should not store the view in the model. This is kind of a "no no" in MVC. Your model already emits a "remove" event when deleted. Your view should listen to it and behave accordingly.
You must remove your view element from the DOM yourself
This is not handled by backbone.
Other general comments:
Views already are extending Backbone.Events
Use this.model instead of this.address