Cannot read property 'superclass' of undefined / Object #<Object> has no method 'addCls' - extjs

--------------------- Solution ---------------------------
I did a workaround of having to callParent inside my code,
var expandFieldOverride = function(event)
{
//event: collapse, false / expand, true
var fieldset = this;
var arguments = [event];
if(!fieldset.readyToExpand){
Ext.each(profilesPanel.items.items, function(panel, i)
{
if (panel.isProfilePanel)
{
console.log(event);
var field = panel.down('profileform[title=Standard Configuration]').down('fieldset[name='+fieldset.name+']');
field.readyToExpand = true;
field.setExpanded(event);
}
});
}
this.callParent(arguments);
fieldset.readyToExpand = false;
}
-------------------------Initial Problem-------------------------------
I am using ExtJS 4.2.1 and I am trying to override the collapse and expand events of fieldsets. Using collapse and expand didn't work, so I had to directly override setExpanded(). I am trying to achieve the event that when one fieldset is collapsed in a profile panel, so is the other in the other profile panel, and vice versa.
Ext.define('EcoCentral.Configuration.ThermostatProfiles.ProfileOptionsFieldSet',
{
extend: 'Ext.form.FieldSet',
setExpanded: expandFieldOverride,
//expand: expandFieldOverride,
//collapse: collapseFieldOverride,
alias: 'widget.profilefieldset'
});
var expandFieldOverride = function(event)
{
//this.callParent(arguments);
//event: collapse, false / expand, true
var fieldset = this;
var arguments = [event];
Ext.each(profilesPanel.items.items, function(panel, i)
{
if (panel.isProfilePanel)
{
var field = panel.down('profileform[title=Standard Configuration]').down('fieldset[name='+fieldset.name+']');
console.log(field);
//field.callParent(arguments);
field.self.superclass.setExpanded.call(arguments);
}
//this.callParent(arguments);
});
}
If I use 'this.callParent(arguments)' inside the code, I recieve
'Uncaught TypeError: Cannot read property 'superclass' of undefined '
I did some research and tried out this line of code
'field.self.superclass.setExpanded.call(arguments);'
from which I recieve :
'Uncaught TypeError: Object # has no method 'addCls''
Which is a call inside of the setExpanded function in the source.
setExpanded: function(expanded) {
var me = this,
checkboxCmp = me.checkboxCmp,
operation = expanded ? 'expand' : 'collapse';
if (!me.rendered || me.fireEvent('before' + operation, me) !== false) {
expanded = !!expanded;
if (checkboxCmp) {
checkboxCmp.setValue(expanded);
}
if (expanded) {
me.removeCls(me.baseCls + '-collapsed');
} else {
me.addCls(me.baseCls + '-collapsed');
}
me.collapsed = !expanded;
if (expanded) {
delete me.getHierarchyState().collapsed;
} else {
me.getHierarchyState().collapsed = true;
}
if (me.rendered) {
// say explicitly we are not root because when we have a fixed/configured height
// our ownerLayout would say we are root and so would not have it's height
// updated since it's not included in the layout cycle
me.updateLayout({ isRoot: false });
me.fireEvent(operation, me);
}
}
return me;
},
My fieldset is defined by xtype:

You have to use apply. Call is the wrong function. Have a look at this:
What is the difference between call and apply?

Related

Extjs - drag drop restriction, containment

In Extjs, I want to know whether I can restrict the dragging of elements within a specific x,y co-ordinates, just like an option, containment in jQuery-UI.
Currently this is my code:
abc.prototype.initDrag = function(v) {
v.dragZone = new Ext.dd.DragZone(v.getEl(), {
containerScroll : false,
getDragData : function(e) {
var sourceEl = e.getTarget(v.itemSelector, 10);
var t = e.getTarget();
var rowIndex = abc.grid.getView().findRowIndex(t);
var columnIndex = abc.grid.getView().findCellIndex(t);
var abcDragZone = v.dragZone ; //Ext.getCmp('idabcDragZone');
var widthToScrollV = $(window).width()-((columnIndex-1)*100);
var widthToScrollH = $(window).height()-((5-rowIndex)*30);
abcDragZone.setXConstraint(0,widthToScrollV);
abcDragZone.setYConstraint(widthToScrollH,0);
if ((rowIndex !== false) && (columnIndex !== false)) {
if (sourceEl) {
abc.isDragged = true;
def.scriptGrid.isDraggableForObject = false;
def.scriptGrid.dragRowIndex = false;
d = sourceEl.cloneNode(true);
d.id = Ext.id();
d.textContent = "\$" + abc.grid.getColumnModel().getColumnHeader(columnIndex);
return {
ddel : d,
sourceEl : d,
sourceStore : v.store
}
}
}
},
getRepairXY : function() {
return this.dragData.repairXY;
},
});
}
But the problem is that the initdrag is called when the csv sheet is added to DOM. Only when its added that element can be accessed and the individual cells' drag limits can be set. So once I add a csv, the limits are not getting set. If I add it again to DOM then the limits work. Is there an option like the jQuery UI containment for draggable, here in extjs?
edit:
I even tried :
constrainTo( constrainTo, [pad], [inContent] )
body had an id of #abc
when I tried with
dragZoneObj.startDrag = function(){
this.constrainTo('abc');
};
which is a method of the DragZone class. It still did not cover the whole body tag.

ExtJS - Drag column header to other grid

I need keep the default feature of reordering columns and add
possibility drop the column in a second grid, building in the last a list
with the columns of first grid.
I hope has been clear.
I solved the issue extending DropZone. This implementation receive as constructor param the target grid, and this, be in the rbar (docked control) of source grid. The key is set ddGroup to "header-dd-zone-" plus id from source grid. I hope this is useful.
Ext.define('Crud.FilterDropZone', {
extend: 'Ext.dd.DropZone'
, constructor: function() {}
, init: function (grid) {
var me = this;
if (grid.rendered) {
me.grid = grid;
me.ddGroup = 'header-dd-zone-' + grid.up('grid').id;
grid.getView().on({
render: function(v) {
me.view = v;
Crud.FilterDropZone.superclass.constructor.call(me, me.view.el);
},
single: true
});
} else {
grid.on('render', me.init, me, {single: true});
}
}
, getTargetFromEvent: function (e) {
return {};
}
, onNodeDrop: function (nodeData, source, e, data) {
var header = data.header
, store = Ext.getCmp(e.target.id).getStore();
//store.add(new store.RecordType({ property: header.text, value: '', reference: header.dataIndex}));
store.add([[header.text, '', header.dataIndex]]);
}
});

How to report invalid form fields using Backbone.js

I'm using Backbone to manage the state of an HTML form. The Model's role is to handle validation. The View's role is to wrap the HTML form and respond to the change or error events emitted by the model.
Backbone seems to only emit change events when the given field is actually valid. This is causing some really unexpected behavior that makes me thing that I'm doing this wrong.
Here is a summary of what I'm doing:
1. Initial load serializes the form and injects it into the model
2. When an error event is emitted, I generate error nodes next to the invalid field.
3. When a change event is emitted, I remove the error notes next to the (now valid) field.
When a page is rendered with an initially valid form, and a user invalidates a field, the message is displayed as expected; however, the model never updates the field internally. Thus when the user corrects the error, a change event is never emitted.
Example: Initially valid
When a page is rendered with an initially invalid form, things appear to be working fine... but this is only because the model's initial attributes are empty. Correcting the field makes the messages disappear, but if you change it again to an invalid state, the message never disappears.
Example: Initially invalid
What am I doing wrong? Perhaps there's another approach I should be using instead?
My Model
var Foo = Backbone.Model.extend({
validate: function(attr) {
var errors = {};
if (_.isEmpty(attr)) return;
if (attr.foo && attr.foo != 123) {
errors.foo = ['foo is not equal to 123'];
}
if (attr.bar && attr.bar != 456) {
errors.bar = ['bar is not equal to 456'];
}
return _.isEmpty(errors) ? undefined : errors;
}
});
My View
FooForm = Backbone.View.extend({
events: {
'change :input': 'onFieldChange'
},
initialize: function(options) {
this.model.on('error', this.renderErrors, this);
this.model.on('change', this.updateFields, this);
// Debugging only
this.model.on('all', function() {
console.info('[Foo all]', arguments, this.toJSON())
});
this.model.set(this.serialize());
},
onFieldChange: function(event) {
var field = event.target,
name = field.name,
value = field.value;
this.model.set(name, value);
},
renderErrors: function(model, errors) {
_.each(errors, function(messages, fieldName) {
var el = $('#' + fieldName),
alert = $('<div/>').addClass('error');
el.parent().find('.error').remove();
_.each(messages, function(message) {
alert.clone().text(message).insertAfter(el);
});
});
},
updateFields: function(model, options) {
if (!options || !options.changes) return;
_.each(_.keys(options.changes), function(fieldName) {
var el = $('#' + fieldName);
el.parent().find('.error').remove();
});
},
serialize: function() {
var raw = this.$el.find(':input').serializeArray(),
data = {},
view = this;
$.each(raw, function() {
// Get the model's field name from the form field's name
var name = this.name;
if (data[name] !== undefined) {
if (!data[name].push) {
data[name] = [data[name]];
}
data[name].push(this.value || '');
}
else {
data[name] = this.value || '';
}
});
return data;
}
});
You can't validate individual field using native Backbone validation.
In my app I use this validation plugin: https://github.com/thedersen/backbone.validation
Then in your model you add validation rules per each field (it's optional, so you don't need to add this to all models):
var NewReview = Backbone.Model.extend({
initialize: function() {
/* ... */
},
validation: {
summary: {
required: true,
minLength: 10
},
pros: {
required: true,
minLength: 10
},
cons: {
required: true,
minLength: 10
},
overall: function(value) {
var text = $(value).text().replace(/\s{2,}/g, ' ');
if (text.length == 0) text = value;
if (text.length < 20) return "Overall review is too short";
},
rating: {
range: [0.5, 5]
},
product_id: {
required: true
}
}
});
Than in views or elsewhere you can validate either entire model or individual fields:
if (this.model.validate()) { ... }
or
if (this.model.isValid("summary")) { ... }

Extjs Script is not working: throws Exception

/**
* Rajan Y. Rawal
* application.js
* This file is sample file for learing Extjs
*/
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
// create namespace
Ext.namespace('myNameSpace');
// Just to allow this tutorial to work for 1.1 and 2.
//Ext.Ext2 = (Ext.version && (Ext.version.indexOf("2") == 0));
// create application
myNameSpace.app = function() {
var btn1;
var privVar1 = 11;
var btn1Handler = function(button, event) {
alert('privVar1=' + privVar1);
alert('this.btn1Text=' + this.btn1Text);
};
return {
// public properties, e.g. strings to translate
btn1Text: 'Button 1',
// public methods
init: function() {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
}
};
}();
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1',
init: function() {
try {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope: this
});
}
catch(e) {
alert('Error: "' + e.message + '" at line: ' + e.lineNumber);
}
}
});
Where is my fault?
Your script is working. What exactly are you expecting it to do?
You're creating an Ext.button with a handler, but there it is not being told to render anywhere on the page. You should provide a renderTo config option which will tell ExtJS to render the button to a given element...
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope: this,
renderTo: 'element-of-html-div'
});
Also, you're defining and then overriding everything in myNameSpace.app, this is pointless.

Loading content dynamically (panels) in an Ext Js Viewport

Well basically im looking on this problem, i have many components with dinamic stuff that is written in the server side with PHP.
Depending on the user my components will change, based on the role of the user.
So i need to know any ways/examples/info on how to do this.
1- I used the load function EXTJS has, but it clearly says i wont load script only plain text.
2- i used eval() but im a bit scared o this approach, like this example crate layout component (static)
var contentPanel = new Ext.Panel({
frame: true,
style: {marginTop: '10px'},
height: 315,
border: true,
bodyBorder: false,
layout: 'fit',
id: 'contentPanel'
});
var mainPanel = new Ext.Panel({
title: 'Panel Principal',
id: 'mainPanel',
border: true,
frame: true,
width: '50%',
style: {margin: '50px auto 0 auto'},
height: 400,
renderTo: Ext.getBody(),
items: [
{
html: 'Panel 1'
},
{
html: 'Panel 2'
},
contentPanel
]
})
and update the content of the layout with js files written on the server
function receiveContent(options, success, response)
{
var respuesta = response.responseText;
//console.log(respuesta);
eval(respuesta);
//console.log(options.url);
url = options.url;
url = url.substring(0,(url.search(/(\.)/)));
var contenedor = Ext.getCmp('contentPanel');
contenedor.removeAll();
var contenido = Ext.getCmp(url);
contenedor.add(contenido);
contenedor.doLayout();
}
function requestContent(panel)
{
//panel es el nombre del archivo que quiero
Ext.Ajax.request({
url: panel+'.js',
callback: receiveContent
});
}
any other way for this to be done, what i DONT want to do is making a million different components and load them ALL at login time like many people seem to say
To address your questions:
The .load method WILL load script and evaluate it once the content has finished loading, however to accomplish this you will need to set the scripts:true option, an example may be:
my_panel.load({
url: 'url_to_load.php/hmt/html/asp...',
params: {param1: param1value, param2: param2value...etc},
nocache: true,
timeout: 30,
scripts: true
});
Using eval() is fine...but seeing as the scripts:true config option above accomplishes this for javascript in the source file, you shouldnt need to use this.
Hope this helps
You might load JavaScript dynamically using something like like below - there are a hundred variations on the web. In this way, you would avoid the AJAX call and handling the response (and subsequent eval).
var aHeadNode = document.getElementById('head')[0];
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = "someFile.js";
aHeadNode.appendChild(oScript);
What I understood from your question is that, you are looking for dynamic JS file loader with a callback handler i.e. the callback function will be called only when the file is loaded fully. I also faced similar problems at start and after searching a lot and doing some research, I developed the following code, it provides absolute Dynamic JS and CSS file loading functionality :
Class ScriptLoader: (Put it in a separate file and load it at first)
ScriptLoader = function() {
this.timeout = 30;
this.scripts = [];
this.disableCaching = false;
};
ScriptLoader.prototype = {
processSuccess : function(response) {
this.scripts[response.argument.url] = true;
window.execScript ? window.execScript(response.responseText) : window
.eval(response.responseText);
if (response.argument.options.scripts.length == 0) {
}
if (typeof response.argument.callback == 'function') {
response.argument.callback.call(response.argument.scope);
}
},
processFailure : function(response) {
Ext.MessageBox.show({
title : 'Application Error',
msg : 'Script library could not be loaded.',
closable : false,
icon : Ext.MessageBox.ERROR,
minWidth : 200
});
setTimeout(function() {
Ext.MessageBox.hide();
}, 3000);
},
load : function(url, callback) {
var cfg, callerScope;
if (typeof url == 'object') { // must be config object
cfg = url;
url = cfg.url;
callback = callback || cfg.callback;
callerScope = cfg.scope;
if (typeof cfg.timeout != 'undefined') {
this.timeout = cfg.timeout;
}
if (typeof cfg.disableCaching != 'undefined') {
this.disableCaching = cfg.disableCaching;
}
}
if (this.scripts[url]) {
if (typeof callback == 'function') {
callback.call(callerScope || window);
}
return null;
}
Ext.Ajax.request({
url : url,
success : this.processSuccess,
failure : this.processFailure,
scope : this,
timeout : (this.timeout * 1000),
disableCaching : this.disableCaching,
argument : {
'url' : url,
'scope' : callerScope || window,
'callback' : callback,
'options' : cfg
}
});
}
};
ScriptLoaderMgr = function() {
this.loader = new ScriptLoader();
this.load = function(o) {
if (!Ext.isArray(o.scripts)) {
o.scripts = [o.scripts];
}
o.url = o.scripts.shift();
if (o.scripts.length == 0) {
this.loader.load(o);
} else {
o.scope = this;
this.loader.load(o, function() {
this.load(o);
});
}
};
this.loadCss = function(scripts) {
var id = '';
var file;
if (!Ext.isArray(scripts)) {
scripts = [scripts];
}
for (var i = 0; i < scripts.length; i++) {
file = scripts[i];
id = '' + Math.floor(Math.random() * 100);
Ext.util.CSS.createStyleSheet('', id);
Ext.util.CSS.swapStyleSheet(id, file);
}
};
this.addAsScript = function(o) {
var count = 0;
var script;
var files = o.scripts;
if (!Ext.isArray(files)) {
files = [files];
}
var head = document.getElementsByTagName('head')[0];
Ext.each(files, function(file) {
script = document.createElement('script');
script.type = 'text/javascript';
if (Ext.isFunction(o.callback)) {
script.onload = function() {
count++;
if (count == files.length) {
o.callback.call();
}
}
}
script.src = file;
head.appendChild(script);
});
}
};
ScriptMgr = new ScriptLoaderMgr();
Now it can be used this way:
For CSS files loading :
ScriptMgr.loadCss([first.css', 'second.css']);
That is you just need to provide css files path in an array and pass that array to loadCss() function as an argument. No callback is required for CSS files.
For JS file loading :
ScriptMgr.load({
scripts : ['lib/jquery-1.4.2.min.js','lib/jquery.touch-gallery-1.0.0.min.js'],
callback : function() {
//Here you will do those staff needed after the files get loaded
},
scope : this
});
In this case, the same way you entered CSS files, here you just need to put that array of JS files in scripts option. The callback function is called only when all the JS files are loaded successfully. Also, if in any case, the JS files are already loaded in the browser (i.e. already this code is run once), then the control will automatically go to the callback function.

Resources