Reusing extended components in other views with ExtJS4 MVC - model-view-controller

I'm using the mvc architecture method for a ui I'm building. I have a menu that I would like to reuse and would like to know if the following is possible:
File structure:
app/
--->app.js
--->view/
--------->viewport.js
--------->Toolbar.js
--------->SaveMenu.js
viewport.js:
Ext.define('MyApp.view.Viewport', {
extend: 'Ext.container.Viewport',
layout: 'fit',
requires: [
'MyApp.view.Toolbar',
'MyApp.view.SaveMenu',
],
initComponent: function () {
this.items = {
dockedItems: [
{
dock : 'top',
xtype : 'myapptoolbar',
height : 40,
}
]
...
// more config, but this works now.
Toolbar.js
Ext.define ('Myapp.view.Toolbar', {
extend : 'Ext.toolbar.Toolbar',
alias : 'widget.myapptoolbar',
items : [
{
text : 'Save',
// The following doesn't work, is there another way to achieve this?
menu : 'myappsavemenu',
}
]
});
SaveMenu.js
Ext.define ('Myapp.view.SaveMenu', {
extend : 'Ext.menu.Menu',
alias : 'widget.myappsavemenu',
items : [
{
text : 'Save as...',
}
]
});
Is it possible to reuse previously defined view components in other views, and if so how should I go about doing this?
Edit - Forgot the error thrown when using this configuration:
Uncaught TypeError: Cannot set property 'ownerCt' of undefined

Yes, it's perfectly reasonable, but the menu property should be a menu instance, a menu id or an object config.
I would go with:
menu: {xtype: 'myappsavemenu'},
ExtJS docs are available online. The button's menu config is addressed here: http://docs.sencha.com/ext-js/4-0/#!/api/Ext.button.Button-cfg-menu
In general, I'm just getting familiar with Ext.
Also note, the first method, which I've now removed, will give you the same menu instance across all myapptoolbars. This may or may not be a terrible thing depending on what you're doing. I try to stay away from it as it can be hard to debug when problems are introduced.

Related

SAPUI5: Extend Control, renderer has html tags with event

I extend a Control to create a new custom control in UI5 and this control renders a tree as UL items nicely. Now I need to implement a collapse/expand within that tree. Hence my renderer writes a tag like
<a class="json-toggle" onclick="_ontoggle"></a>
and within that _ontoggle function I will handle the collapse/expand logic.
No matter where I place the _ontoggle function in the control, I get the error "Uncaught ReferenceError: _ontoggle is not defined"
I am missing something obvious but I can't find what it is.
At the moment I have placed a function inside the
return Control.extend("mycontrol",
{_onToggle: function(event) {},
...
Please note that this event is not one the control should expose as new event. It is purely for the internals of how the control reacts to a click event.
I read things about bind and the such but nothing that made sense for this use case.
Took me a few days to crack that, hence would like to provide you with a few pointers.
There are obviously many ways to do that, but I wanted to make that as standard as possible.
The best suggestion I found was to use the ui5 Dialog control as sample. It consists of internal buttons and hence is similar to my requirement: Render something that does something on click.
https://github.com/SAP/openui5/blob/master/src/sap.ui.commons/src/sap/ui/commons/Dialog.js
In short, the solution is
1) The
<a class="json-toggle" href></a>
should not have an onclick. Neither in the tag nor by adding such via jQuery.
2) The control's javascript code should look like:
sap.ui.define(
[ 'sap/ui/core/Control' ],
function(Control) {
var control = Control.extend(
"com.controlname",
{
metadata : {
...
},
renderer : function(oRm, oControl) {
...
},
init : function() {
var libraryPath = jQuery.sap.getModulePath("mylib");
jQuery.sap.includeStyleSheet(libraryPath + "/MyControl.css");
},
onAfterRendering : function(arguments) {
if (sap.ui.core.Control.prototype.onAfterRendering) {
sap.ui.core.Control.prototype.onAfterRendering.apply(this, arguments);
}
},
});
control.prototype.onclick = function (oEvent) {
var target = oEvent.target;
return false;
};
return control;
});
Nothing in the init(), nothing in the onAfterRendering(), renderer() outputs the html. So far there is nothing special.
The only thing related with the onClick is the control.prototype.onclick. The variable "target" is the html tag that was clicked.

ExtJS: Clear a data binding from a viewController

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!

Send parameters from a view to other in Sencha Touch 2 using a controller

I'm just migrating from Sencha Touch 1.x to Sencha Touch 2 and I can't find a way to pass parameters between views.
Lets say I have to views:
Place (a list with all the places)
PeopleAtPlace (A list of the people for each place)
Now what I need to do is to pass the id of the place that is pressed to the peopleatplace view so it can get the people for that specific view.
I've been reading Sencha's documentation but it's pretty confusing to me.
Could somebody please help me? A code snippet would be great help to me.
Controllers can be the glue between different views. I don't know what kind of views you exactly have, but the following code can serve as a base:
Ext.define('MyApp.controller.TestController', {
extend : 'Ext.app.Controller',
config : {
views : [ // you need to list all views your controller will use
'Place',
'PeopleAtPlace'
],
refs : {
place : 'place', // ComponentQuery used to find the view e.g. xtype, id, etc of the view
peopleAtPlace : 'peopleAtPlace'
},
control : {
place : {
select : 'onPlaceSelected' // use the appropriate event
}
}
},
onPlaceSelected : function (view, record) {
var peopleAtPlaceView = this.getPeopleAtPlace(); // generated by Sencha from the ref property
// now you have the reference to the target view, you can put your logic here
peopleAtPlaceView.doSomething(record);
}
});

ExtJS MVC : how to remove handler from dynamic controller

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!

Viewport Apply Conditional Toolbar with Sencha Touch

Im going to MVC route with Sencha. I have a Viewport panel initialized much like the twitter example:
/**
* #author Jeff Blake
*/
Ext.regApplication('App', {
defaultTarget: 'viewport',
defaultUrl : 'Viewport/index',
name : 'App',
icon : "mobile/public/resources/images/icon.png",
phoneStartupScreen : "mobile/public/resources/images/phone_startup.png",
//useHistory : false,
//useLoadMask : true,
launch: function() {
Ext.Viewport.init();
Ext.Viewport.onOrientationChange();
this.viewport = new App.Viewport({
application: this
});
Ext.dispatch({
controller: 'User',
action : 'index'
});
}
});
/**
* #class App.Viewport
* #extends Ext.Panel
* This is a default generated class which would usually be used to initialize your application's
* main viewport. By default this is simply a welcome screen that tells you that the app was
* generated correctly.
*/
App.Viewport = Ext.extend(Ext.Panel, {
id : 'viewport',
layout : 'card',
fullscreen: true,
cardSwitchAnimation: 'slide',
initComponent: function() {
Ext.apply(this, {
dockedItems: [
{
// Top Bar
dock : 'top',
xtype : 'toolbar',
title : 'Whats Good',
items: [
{
text: 'About'
},
]
}
]
});
App.Viewport.superclass.initComponent.apply(this, arguments);
}
});
Ext.reg('App.Viewport', App.Viewport);
New Code:
if (!App.viewport.getDockedComponent(homeBar)) {
var homeBar = new App.HomeBar();
App.viewport.addDocked(homeBar);
}
I want to be able to conditionally apply DockedItems (toolbars) based on which Type of panel is currently rendered in the Viewport. EG: one for Login, Home screen, detail screen, etc.
I've tried using Ext.apply(App.Viewport, { dockedItems: [App.LoginBar]});
But that doesn't work. Currently it works to add the toolbar to the currently rendered Panel and setting it to fullscreen, but unfortunately transitions and things behave weirdly as the structure is
Panel
Toolbar
Panel
Toolbar
/end Panel
/end Panel
Does anyone have a suggestion?
To programmatically add a docked item, I would recommend using
viewport.addDocked(loginBar);
Methods like this are far better than trying to update the original configuration of the component.
Then there is also a .removeDocked() method to take it off again.
Also make sure you are dealing with instances of the components, not trying to update their class.
To get the reference to your application's viewport, you can come in through the 'App' namespace, which is automatically created by the name property of the regApplication config.
So you could make your toolbar button do this for example:
{
text: 'About',
handler: function() {
App.viewport.getDockedItems()[0].setTitle('Pressed!');
}
},
Which would make the title change when you press the button.
But now I better understand what it is you are trying to do, I recommend you don't dock a single, dynamically-changed toolbar to the outer viewport, but add individual toolbars to each of the (card) panels in it. That way they get to slide nicely too ;-)

Resources