How to use smartimage or smartfile in compositefield? - extjs

I would like to composite some fields (textField, URL and image). I read some tutorial about how to composite some elements with the
CQ.Ext.form.CompositeField widget.
my javascript looks like this CustomPathField.js:
/**
* #class VRBANKENPORTAL.CustomPathFieldWidget
* #extends CQ.form.CompositeField
* This is a custom path field with link text and target
* #param {Object} config the config object
*/
/**
* #class Ejst.CustomWidget
* #extends CQ.form.CompositeField
* This is a custom widget based on {#link CQ.form.CompositeField}.
* #constructor
* Creates a new CustomWidget.
* #param {Object} config The config object
*/
VRBANKENPORTAL.CustomPathFieldWidget = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #private
* #type CQ.Ext.form.TextField
*/
hiddenField: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
linkText: null,
/**
* #private
* #type CQ.form.SmartImage
*/
smartImage: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
linkURL: null,
/**
* #private
* #type CQ.Ext.form.CheckBox
*/
openInNewWindow: null,
/**
* #private
* #type CQ.Ext.form.FormPanel
*/
formPanel: null,
constructor: function (config) {
config = config || {};
var defaults = {
"border": true,
"labelWidth": 75,
"layout": "form"
//"columns":6
};
config = CQ.Util.applyDefaults(config, defaults);
VRBANKENPORTAL.CustomPathFieldWidget.superclass.constructor.call(this, config);
},
//overriding CQ.Ext.Component#initComponent
initComponent: function () {
VRBANKENPORTAL.CustomPathFieldWidget.superclass.initComponent.call(this);
// Hidden field
this.hiddenField = new CQ.Ext.form.Hidden({
name: this.name
});
this.add(this.hiddenField);
// Link text
this.add(new CQ.Ext.form.Label({
cls: "customwidget-label",
text: "Link Text"
}));
this.linkText = new CQ.Ext.form.TextField({
cls: "customwidget-1",
fieldLabel: "Link Text: ",
maxLength: 80,
maxLengthText: "A maximum of 80 characters is allowed for the Link Text.",
allowBlank: true,
listeners: {
change: {
scope: this,
fn: this.updateHidden
}
}
});
this.add(this.linkText);
// Link SmartImage
this.smartImage = new CQ.form.SmartImage({
cls: "customwidget-smartimage",
title: "Image",
allowUpload: true,
fileNameParameter: "./image/fileName",
fileReferenceParameter: "./image/fileReference",
height: 100,
mimeTypes: "*.jpg;*.jpeg;*.gif;*.png",
name: "./image/file",
fieldLabel: "Banner"
});
this.add(this.smartImage);
// Link URL
this.add(new CQ.Ext.form.Label({
cls: "customwidget-label",
text: "Überschrift"
}));
this.linkURL = new CQ.form.BrowseField({
cls: "customwidget-2",
fieldLabel: "URL: ",
allowBlank: false,
width: 225,
listeners: {
change: {
scope: this,
fn: this.updateHidden
},
dialogclose: {
scope: this,
fn: this.updateHidden
}
}
});
this.add(this.linkURL);
// Link openInNewWindow
this.openInNewWindow = new CQ.Ext.form.Checkbox({
cls: "customwidget-3",
boxLabel: "New window",
listeners: {
change: {
scope: this,
fn: this.updateHidden
},
check: {
scope: this,
fn: this.updateHidden
}
}
});
this.add(this.openInNewWindow);
},
processInit: function (path, record) {
this.linkText.processInit(path, record);
this.smartImage.processInit(path, record);
this.linkURL.processInit(path, record);
this.openInNewWindow.processInit(path, record);
},
setValue: function (value) {
var link = JSON.parse(value);
this.linkText.setValue(link.text);
this.smartImage.setValue(link.smartImage);
this.linkURL.setValue(link.url);
this.openInNewWindow.setValue(link.openInNewWindow);
this.hiddenField.setValue(value);
},
getValue: function () {
return this.getRawValue();
},
getRawValue: function () {
var link = {
"url": this.linkURL.getValue(),
"text": this.linkText.getValue(),
"smartImage": this.smartImage.getValue(),
"openInNewWindow": this.openInNewWindow.getValue()
};
return JSON.stringify(link);
},
updateHidden: function () {
this.hiddenField.setValue(this.getValue());
}
});
CQ.Ext.reg('mypathfield', VRBANKENPORTAL.CustomPathFieldWidget);
my Dialog.xml looks like this:
...
....
<items jcr:primaryType="cq:WidgetCollection">
<links
jcr:primaryType="cq:Widget"
fieldDescription="Press + to add more links"
fieldLabel="Links"
hideLabel="true"
name="./links"
width="1000"
xtype="multifield">
<fieldConfig
jcr:primaryType="cq:Widget"
xtype="mypathfield"/>
<listeners
jcr:primaryType="nt:unstructured" />
</links>
</items>
...
.....
the result dispayed in the dialog can be seen in the following screenshot.
my Question is: How to use the smartImage or smartFile in CompositeField? What did I do wrong?

I have not tried this with a smartimage widget, but I've found that the ACS Commons multifieldpanel is superior to the multifield.
Have a look:
http://adobe-consulting-services.github.io/acs-aem-commons/features/widgets.html#multi-field-panel-since-150

Related

How can I retrieve the cursor position in an ExtJS text area field?

I defined a text area field in an extJS window as follows:
me.myTextArea = Ext.create({
xtype: 'textareafield',
width: 500,
height: 500,
editable: true,
selectOnFocus: false,
listeners: {
afterrender: function() {
this.focus(true);
let cursorPos = this.getValue().length;
this.selectText(cursorPos, cursorPos);
}
}
});
I added the text area field to a panel contained within a window, and I set the text area field as focus element. I prevented the text there to be selected after the textarea field's being rendered. I would like to add the following feature. On closing the window, I will be able to get the position the cursor has within the text area field. So far, my attemps at resolving the problem were withou success. I set up an alert as follows:
listeners: {
'close': function(me) {
alert(me.getCaretPos(cmp.myTextArea.getEl().getId()));
}
},
Now the function named "getCaretPos" is designed to get the cursor position. I did not invent the function, but I found in on the net. Haplessly, the function does not work, it always returns -1:
getCaretPos: function(id){
var el = document.getElementById(id);
var rng, ii=-1;
if(typeof el.selectionStart=="number") {
ii=el.selectionStart;
} else if (document.selection && el.createTextRange){
rng=document.selection.createRange();
rng.collapse(true);
rng.moveStart("character", -el.value.length);
ii=rng.text.length;
}
return ii;
}
Especially, I do not undertsand, why "el.selectionStart" is always undefined in the function. I would be very glad if somebody could help me in resolving this mystery.
In this FIDDLE, I have created a custometextarea using extend:Ext.form.field.TextArea and putted some custom method. I hope this will help/guide you to achieve your requirements.
I have checked this code is working in ExtJS 4.x and later.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('CustomeTextArea', {
extend: 'Ext.form.field.TextArea',
xtype: 'customtextarea',
inputTypeSelectionSupported: /text|password|search|tel|url/i,
selectDir: {
b: 'backward',
back: 'backward',
f: 'forward'
},
/*
* this event will call get the cursoe postion inside of field
* #return {NUMBER}
*/
getCaretPos: function () {
var dom = this.inputEl.dom,
pos, selection;
if (this.inputTypeSelectionSupported.test(dom.type)) {
pos = dom.selectionStart;
selection = (typeof pos !== 'number') && this.getTextSelection();
if (selection) {
pos = selection[0];
}
} else {
Ext.raise('Input type of "' + dom.type + '" does not support selectionStart');
}
return pos;
},
/*
* this event will call selectText event
* #params {NUMBER} pos
*/
setCaretPos: function (pos) {
this.selectText(pos, pos);
},
/*
* #params {NUMBER} start
* #params {NUMBER} end
* #params {STRING} direction to select it is optional
*/
selectText: function (start, end, direction) {
var me = this,
range,
dom = me.inputEl.dom,
len;
if (dom && me.inputTypeSelectionSupported.test(dom.type)) {
start = start || 0;
len = dom.value.length;
if (end === undefined) {
end = len;
}
direction = me.selectDir[direction] || direction || 'forward';
if (dom.setSelectionRange) {
dom.setSelectionRange(start, end, direction);
} else if (dom.createTextRange) {
range = dom.createTextRange();
range.moveStart('character', start);
range.moveEnd('character', -(len - end));
range.select();
}
} else if (!me.inputTypeSelectionSupported.test(dom.type)) {
Ext.raise('Input type of "' + dom.type + '" does not support setSelectionRange');
}
return me;
},
//This event will select the text inside of textfield/textarea
getTextSelection: function () {
var dom = this.inputEl.dom;
if (this.inputTypeSelectionSupported.test(dom.type)) {
return [
dom.selectionStart,
dom.selectionEnd,
dom.selectionDirection
];
} else {
Ext.raise('Input type of "' + this.dom.type + '" does not support selectionStart, selectionEnd and selectionDirection');
return [];
}
}
});
Ext.create('Ext.window.Window', {
title: 'cursor position',
height: 200,
width: 400,
layout: 'fit',
items: [{
xtype: 'customtextarea',
margin: 5,
grow: true,
name: 'message',
fieldLabel: 'Message',
labelAlign: 'top',
value: 'How can I retrieve the cursor position in an ExtJS text area field?',
anchor: '100%',
listeners: {
afterrender: function (cmp) {
Ext.defer(function () {
cmp.focus(false); //if you pass true then it will automatically select text inside of field
let cursorPos = cmp.getValue().length;
cmp.selectText(0, cursorPos - 5, 'b');
}, 50)
}
}
}],
listeners: {
beforeclose: function (win) {
var textArea = win.down('customtextarea'),
pos = textArea.getCaretPos();
Ext.Msg.alert('Success', `This is your cursor postion <b>${pos}</b>`)
}
}
}).show();
}
});

Treelist Store not loaded before render

Using on the dashboard example, i'm trying to generate a treelist menu, based on user privileges.
After successfully log in, the main view is generated. The main, contains in the west region the treelist menu and next to it, the data panel. The navigation is done by using hashtags. The problem apear on refresh or in the first initialization. Actually, i noticed that the navigation store is loaded after the view is rendered.
How / where do i get to load the navigation store, so on refresh or first initalization of the view, i can get it and using it to match the routes?
Thanks,
M
My main view looks like this:
Ext.define('app.view.main.Main', {
extend: 'Ext.container.Viewport',
xtype: 'app-main',
id:'app-main',
requires: [
'Ext.button.Segmented',
'Ext.list.Tree'
],
controller: 'main',
viewModel: 'main',
cls: 'sencha-dash-viewport',
itemId: 'mainView',
layout: {
type: 'vbox',
align: 'stretch'
},
listeners: {
render: 'onMainViewRender'
},
items: [
{
xtype: 'toolbar',
cls: 'sencha-dash-dash-headerbar shadow',
height: 64,
itemId: 'headerBar',
items: [
{
xtype: 'tbtext',
text: localStorage.getItem('Name'),
cls: 'top-user-name'
},
{
xtype: 'image',
cls: 'header-right-profile-image',
height: 35,
width: 35,
alt:'current user image',
src: 'resources/images/user-profile/mb.jpg'
}
]
},
{
xtype: 'maincontainerwrap',
id: 'main-view-detail-wrap',
reference: 'mainContainerWrap',
flex: 1,
items: [
{
xtype: 'treelist',
reference: 'navigationTreeList',
itemId: 'navigationTreeList',
width: 250,
expanderFirst: false,
expanderOnly: true,
ui: 'navigation',
bind: '{navItems}',
listeners: {
selectionchange: 'onNavigationTreeSelectionChange'
}
},
{
xtype: 'container',
reference: 'mainCardPanel',
flex:1,
cls: 'sencha-dash-right-main-container',
itemId: 'contentPanel',
layout: {
type: 'card',
anchor: '100%'
}
}
]
}
]
});
The viewmodel:
Ext.define('app.view.main.MainModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.main',
stores: {
navItems: {
type: 'tree',
storeId: 'NavigationTree',
name: 'NavigationTree',
root: {
expanded: true
},
autoLoad: false,
proxy: {
type: 'ajax',
url: 'php.php',
reader: {
type: 'json',
idProperty: 'id',
messageProperty: 'msg'
}
}
}
}
});
And the viewcontroller:
Ext.define('app.view.main.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
listen : {
controller : {
'#' : {
unmatchedroute : 'onRouteChange'
}
}
},
routes: {
':node': 'onRouteChange'
},
lastView: null,
setCurrentView: function(hashTag) {
hashTag = (hashTag || '').toLowerCase();
var me = this,
refs = me.getReferences(),
mainCard = refs.mainCardPanel,
mainLayout = mainCard.getLayout(),
navigationList = refs.navigationTreeList,
store = me.getViewModel().getStore('navItems');
//store = navigationList.getStore();
var node = store.findNode('routeId', hashTag) ||
store.findNode('viewType', hashTag);
var view = (node && node.get('viewType')) ,
lastView = me.lastView,
existingItem = mainCard.child('component[routeId=' + hashTag + ']'),
newView;
// Kill any previously routed window
if (lastView && lastView.isWindow) {
lastView.destroy();
}
lastView = mainLayout.getActiveItem();
if (!existingItem) {
newView = Ext.create({
xtype: view,
routeId: hashTag, // for existingItem search later
hideMode: 'offsets'
});
}
if (!newView || !newView.isWindow) {
// !newView means we have an existing view, but if the newView isWindow
// we don't add it to the card layout.
if (existingItem) {
// We don't have a newView, so activate the existing view.
if (existingItem !== lastView) {
mainLayout.setActiveItem(existingItem);
}
newView = existingItem;
}
else {
// newView is set (did not exist already), so add it and make it the
// activeItem.
Ext.suspendLayouts();
mainLayout.setActiveItem(mainCard.add(newView));
Ext.resumeLayouts(true);
}
}
navigationList.setSelection(node);
if (newView.isFocusable(true)) {
newView.focus();
}
me.lastView = newView;
},
onNavigationTreeSelectionChange: function (tree, node) {
var to = node && (node.get('routeId') || node.get('viewType'));
if (to) {
this.redirectTo(to);
}
},
onToggleNavigationSize: function () {
var me = this,
refs = me.getReferences(),
navigationList = refs.navigationTreeList,
wrapContainer = refs.mainContainerWrap,
collapsing = !navigationList.getMicro(),
new_width = collapsing ? 64 : 250;
if (Ext.isIE9m || !Ext.os.is.Desktop) {
Ext.suspendLayouts();
refs.senchaLogo.setWidth(new_width);
navigationList.setWidth(new_width);
navigationList.setMicro(collapsing);
Ext.resumeLayouts(); // do not flush the layout here...
// No animation for IE9 or lower...
wrapContainer.layout.animatePolicy = wrapContainer.layout.animate = null;
wrapContainer.updateLayout(); // ... since this will flush them
}
else {
if (!collapsing) {
// If we are leaving micro mode (expanding), we do that first so that the
// text of the items in the navlist will be revealed by the animation.
navigationList.setMicro(false);
}
// Start this layout first since it does not require a layout
refs.senchaLogo.animate({dynamic: true, to: {width: new_width}});
// Directly adjust the width config and then run the main wrap container layout
// as the root layout (it and its chidren). This will cause the adjusted size to
// be flushed to the element and animate to that new size.
navigationList.width = new_width;
wrapContainer.updateLayout({isRoot: true});
navigationList.el.addCls('nav-tree-animating');
// We need to switch to micro mode on the navlist *after* the animation (this
// allows the "sweep" to leave the item text in place until it is no longer
// visible.
if (collapsing) {
navigationList.on({
afterlayoutanimation: function () {
navigationList.setMicro(true);
navigationList.el.removeCls('nav-tree-animating');
},
single: true
});
}
}
},
onMainViewRender:function() {
if (!window.location.hash) {
this.redirectTo("dashboard");
}
},
onRouteChange:function(id){
this.setCurrentView(id);
},
onSearchRouteChange: function () {
this.setCurrentView('searchresults');
},
onSwitchToModern: function () {
Ext.Msg.confirm('Switch to Modern', 'Are you sure you want to switch toolkits?',
this.onSwitchToModernConfirmed, this);
},
onSwitchToModernConfirmed: function (choice) {
if (choice === 'yes') {
var s = location.search;
// Strip "?classic" or "&classic" with optionally more "&foo" tokens
// following and ensure we don't start with "?".
s = s.replace(/(^\?|&)classic($|&)/, '').replace(/^\?/, '');
// Add "?modern&" before the remaining tokens and strip & if there are
// none.
location.search = ('?modern&' + s).replace(/&$/, '');
}
},
onAfterRender: function(){
console.log('after render');
}
});
I kinda solve it using "before" action in router with a method that waits for the store to load.
routes: {
':node':{
before: 'wait',
action: 'onRouteChange'
}
and the method:
wait : function() {
var args = Ext.Array.slice(arguments),
action = args.pop(),
store = Ext.getStore('NavigationTree');
if (store.loading) {
store.on('load', action.resume, action);
} else {
action.resume();
}
}
In viewcontroller
//...
var me = this,
refs = me.getReferences(),
mainCard = refs.mainCardPanel,
mainLayout = mainCard.getLayout(),
navigationList = refs.navigationTreeList,
viewModel = me.getViewModel(),
vmData = viewModel.getData(),
store = navigationList.getStore(),
//store = Ext.getStore('NavigationTree'),
node = store.findNode('routeId', hashTag),
view = node ? node.get('view') : null,
lastView = vmData.currentView,
existingItem = mainCard.child('component[routeId=' + hashTag + ']'),
newView;
// BEGIN ADD THIS
if(!view) {
var viewTag = hashTag.charAt(0).toUpperCase() + hashTag.slice(1);
view = hashTag + "." + viewTag;
if(!Ext.ClassManager.getAliasesByName('Fruileg3.view.' + view)) view = '';
}
// END
// Kill any previously routed window
if (lastView && lastView.isWindow) {
lastView.destroy();
}
//...

How to override a function of extjs in CQ/AEM?

I'm using selection Xtype and checkbox type property (CQ.form.Selection) to create a checkbox group in CQ5 (API docs at http://docs.adobe.com/docs/en/cq/5-6/widgets-api/index.html?class=CQ.form.Selection).
But I want to override setValue, getValue and validate functions of it in order to meet my requirement. How can I do that via JCR node declaration ?
many thanks and appreciate.
Not sure what you mean by "do that via JCR node declaration". But if you need to make a few additional steps with standard xtype you just need to create a custom xtype, which wraps the standard one, and use it.
Here is an example of JS-code that creates and registers new xtype (combines values of 2 different fields into a single value).
Ejst.CustomWidget = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #private
* #type CQ.Ext.form.TextField
*/
hiddenField: null,
/**
* #private
* #type CQ.Ext.form.ComboBox
*/
allowField: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
otherField: null,
constructor: function(config) {
config = config || { };
var defaults = {
"border": false,
"layout": "table",
"columns":2
};
config = CQ.Util.applyDefaults(config, defaults);
Ejst.CustomWidget.superclass.constructor.call(this, config);
},
// overriding CQ.Ext.Component#initComponent
initComponent: function() {
Ejst.CustomWidget.superclass.initComponent.call(this);
this.hiddenField = new CQ.Ext.form.Hidden({
name: this.name
});
this.add(this.hiddenField);
this.allowField = new CQ.form.Selection({
type:"select",
cls:"ejst-customwidget-1",
listeners: {
selectionchanged: {
scope:this,
fn: this.updateHidden
}
},
optionsProvider: this.optionsProvider
});
this.add(this.allowField);
this.otherField = new CQ.Ext.form.TextField({
cls:"ejst-customwidget-2",
listeners: {
change: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.otherField);
},
// overriding CQ.form.CompositeField#processPath
processPath: function(path) {
console.log("CustomWidget#processPath", path);
this.allowField.processPath(path);
},
// overriding CQ.form.CompositeField#processRecord
processRecord: function(record, path) {
console.log("CustomWidget#processRecord", path, record);
this.allowField.processRecord(record, path);
},
// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
var parts = value.split("/");
this.allowField.setValue(parts[0]);
this.otherField.setValue(parts[1]);
this.hiddenField.setValue(value);
},
// overriding CQ.form.CompositeField#getValue
getValue: function() {
return this.getRawValue();
},
// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {
if (!this.allowField) {
return null;
}
return this.allowField.getValue() + "/" +
this.otherField.getValue();
},
// private
updateHidden: function() {
this.hiddenField.setValue(this.getValue());
}
});
// register xtype
CQ.Ext.reg('ejstcustom', Ejst.CustomWidget);
Creating custom xtype in AEM6's touch UI

CQ, custom widget for text, link, checkbox in multifield

please see the image below, this is the component i am trying to build. a custom widget with text, link and checkbox.
so far, i can manage to get interface party showing up correctly, as you can see in the image.
but, soon as i click 'ok' it will throw me a 'Uncaught TypeError: undefined is not a function'
as showed in red underline in image...so obviously it wont be able to pass any object data into JSP layer for me to use later....
so, basically, it complaining about that 'vt' is undefined.... i dont have a clud why it is happening...
here is my code for generating the widget
/**
* #class MapLinks.CustomWidget
* #extends CQ.form.CompositeField
* This is a custom widget based on {#link CQ.form.CompositeField}.
* #constructor
* Creates a new CustomWidget.
* #param {Object} config The config object
*/
CQ.form.KeyValueLinkSelection = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #private
* #type CQ.Ext.form.TextField
*/
hiddenField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
textLabel: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
textField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
pathLabel: null,
/**
* #private
* #type CQ.form.PathField
*/
pathField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
checkboxLabel: null,
/**
* #private
* #type CQ.Ext.form.Checkbox
*/
checkbox: null,
constructor: function(config) {
config = config || { };
var defaults = {
"border": false,
"layout": "table",
"columns": 6
};
config = CQ.Util.applyDefaults(config, defaults);
CQ.form.KeyValueLinkSelection.superclass.constructor.call(this, config);
},
// overriding CQ.Ext.Component#initComponent
initComponent: function() {
CQ.form.KeyValueLinkSelection.superclass.initComponent.call(this);
this.hiddenField = new CQ.Ext.form.Hidden({
name: this.name
});
this.add(this.hiddenField);
this.textLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-1",
text:"Text:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:10px;"
});
this.add(this.textLabel);
this.textField = new CQ.Ext.form.TextField({
cls:"keyvaluelinkselection-2",
listeners: {
change: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.textField);
this.pathLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-3",
text:"Link:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:15px;"
});
this.add(this.pathLabel);
this.pathField = new CQ.form.PathField({
cls:"keyvaluelinkselection-4",
width:"150",
listeners: {
change: {
scope:this,
fn:this.updateHidden
},
dialogselect: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.pathField);
this.checkboxLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-5",
text:"open in new tab:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:20px;"
});
this.add(this.checkboxLabel);
this.checkbox = new CQ.Ext.form.Checkbox({
cls:"keyvaluelinkselection-6",
width:"30",
listeners: {
change: {
scope:this,
fn:this.updateHidden
},
dialogselect: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.checkbox);
},
// overriding CQ.form.CompositeField#processPath
processPath: function(path) {
this.textField.processPath(path);
},
// overriding CQ.form.CompositeField#processRecord
processRecord: function(record, path) {
this.textField.processRecord(record, path);
},
// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
var parts = value.split("}={");
this.textField.setValue(parts[0]);
this.pathField.setValue(parts[1]);
this.checkbox.setValue(parts[2]);
this.hiddenField.setValue(value);
},
// overriding CQ.form.CompositeField#getValue
getValue: function() {
return this.getRawValue();
},
// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {
return this.textField.getValue() + "}={" +
this.pathField.getValue() + "}={" +
this.checkbox.getValue();
},
// private
updateHidden: function() {
this.hiddenField.setValue(this.getValue());
}
});
// register xtype
CQ.Ext.reg('keyvaluelinkselection', CQ.form.KeyValueLinkSelection);
bascially, i was flowing the tutorial giving by adobe -> http://helpx.adobe.com/experience-manager/using/dynamically-updating-aem-custom-xtype.html
any hints will be helpful, thanks
i actually figured out myself....
basically, just override validateValue: function(value)
here is the whole code example:
CQ.form.KeyValueLinkSelection = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #private
* #type CQ.Ext.form.TextField
*/
hiddenField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
textLabel: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
textField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
pathLabel: null,
/**
* #private
* #type CQ.form.PathField
*/
pathField: null,
/**
* #private
* #type CQ.Ext.form.Label
*/
checkboxLabel: null,
/**
* #private
* #type CQ.Ext.form.Checkbox
*/
checkbox: null,
constructor: function(config) {
config = config || { };
var defaults = {
"border": false,
"layout": "table",
"columns": 6
};
config = CQ.Util.applyDefaults(config, defaults);
CQ.form.KeyValueLinkSelection.superclass.constructor.call(this, config);
},
// overriding CQ.Ext.Component#initComponent
initComponent: function() {
CQ.form.KeyValueLinkSelection.superclass.initComponent.call(this);
this.hiddenField = new CQ.Ext.form.Hidden({
name: this.name
});
this.add(this.hiddenField);
this.textLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-1",
text:"Text:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:10px;"
});
this.add(this.textLabel);
this.textField = new CQ.Ext.form.TextField({
cls:"keyvaluelinkselection-2",
allowBlank: false,
listeners: {
change: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.textField);
this.pathLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-3",
text:"Link:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:15px;"
});
this.add(this.pathLabel);
this.pathField = new CQ.form.PathField({
cls:"keyvaluelinkselection-4",
width:"150",
allowBlank: false,
listeners: {
change: {
scope:this,
fn:this.updateHidden
},
dialogselect: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.pathField);
this.checkboxLabel = new CQ.Ext.form.Label({
cls:"keyvaluelinkselection-5",
text:"open in new tab:",
style:"font-size: 12px; font-family: Arial, Verdana, sans-serif; vertical-align:text-bottom; padding-bottom:0px; padding-right:5px; padding-left:20px;"
});
this.add(this.checkboxLabel);
this.checkbox = new CQ.Ext.form.Checkbox({
cls:"keyvaluelinkselection-6",
width:"30",
listeners: {
change: {
scope:this,
fn:this.updateHidden
},
dialogselect: {
scope:this,
fn:this.updateHidden
}
}
});
this.add(this.checkbox);
},
validateValue: function(value) {
if(value.length < 1){ // if it's blank
if(this.allowBlank){
this.clearInvalid();
return true;
}else{
this.markInvalid(this.blankText);
return false;
}
}
if(this.vtype){
//TO DO, may need this condition checker later on
}
if(typeof this.validator == "function"){
var msg = this.validator(value);
if(msg !== true){
this.markInvalid(msg);
return false;
}
}
if(this.regex && !this.regex.test(value)){
this.markInvalid(this.regexText);
return false;
}
return true;
},
// overriding CQ.form.CompositeField#processPath
processPath: function(path) {
this.textField.processPath(path);
},
// overriding CQ.form.CompositeField#processRecord
processRecord: function(record, path) {
this.textField.processRecord(record, path);
},
// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
var parts = value.split("|");
this.textField.setValue(parts[0]);
this.pathField.setValue(parts[1]);
this.checkbox.setValue(parts[2]);
this.hiddenField.setValue(value);
},
// overriding CQ.form.CompositeField#getValue
getValue: function() {
return this.getRawValue();
},
// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {
return this.textField.getValue() + "|" +
this.pathField.getValue() + "|" +
this.checkbox.getValue();
},
// private
updateHidden: function() {
this.hiddenField.setValue(this.getValue());
}
});
// register xtype
CQ.Ext.reg('keyvaluelinkselection', CQ.form.KeyValueLinkSelection);
and it 100% works....

Filter store on combobox selection

I have tried this different ways, but still can't get the filter to work. My ext app lets user to choose a single state from a combobox, and the grid below displays more data on that selected "value"=state.. On select, the combobox fires a function that filters the store of the grid and updates the store...
this is my store for the grid...
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
id: 'OurData',
pageSize: 20,
pageNumber: 1,
remoteSort: true,
fields: [
{ name: 'States' },
{ name: 'FullName' },
{ name: 'Capital' },
{ name: 'Population' }
],
proxy: {
type: 'ajax',
url: 'GetState/getS',
reader: {
type: 'json',
root: 'myTable',
idProperty: 'States',
totalProperty: '#count'
}
}
});
store.loadPage(1);
this is my combobox
xtype: 'combo',
id: 'iccombo',
scope: this,
store: this.Combostore,
fieldLabel: 'Short State',
displayField: 'States',
valueField: 'States',
typeAhead: true,
triggerAction: 'all',
queryMode: 'remote',
name: 'State',
labelWidth: 125,
anchor: '95%',
listeners: {
scope: this,
select: this.fireFilter
}
and this is where the filter should take place...
fireFilter: function (value) {
// Get passed value
this.selectedC = value.getValue();
console.log('selectedValue: ' + this.selectedC);
// Clear existing filters
this.store.clearFilter(false);
// Build filter
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (item) {
var fieldNames = item.fields.keys;
for (var j = 0; j < fieldNames.length; j++) {
var fieldName = fieldNames[j];
if (item.data[fieldName] != null) {
var stringVal = item.data[fieldName].toString();
if (stringVal != null && stringVal.toLowerCase().indexOf(value.toLowerCase()) != -1) {
return true;
}
}
}
return false;
}
});
// Apply filter to store
this.store.filter(myfilter);
}
when I run the code, it display all data in the grid, and on selection of combobox, it still shows the same data..
For some reason, the code never runs through the filterFn... because my console.log doesn't show up
this is what I got in firebug's response
_dc 1352902173425
filter [{"property":null,"value":null}]
limit 20
page 1
start 0
as you can clearly see, the selected 'value' is null, but my 'console.log' prints the value selected... I think the way I am getting the passed value and applying the filter is incorrect... can someone please take a look... thanks
UPDATE... I am able to get inside the function and my console.log shows all the fields... but once I get to the last if statement... I get this error
TypeError: value.toLowerCase is not a function
What am I doing wrong here? Thanks
In addition to dbrin's anwser I also can't understand why you are using remoteSort but not remoteFilter? You may also have a scope issue by using this.
Anyway I would recommend you to extend a new combo type so that you are also be able to clear your filter if you have the need to. Here is an extension I have written for my own use. Note that the filtering itself needs to be implemented in the onSearch method, which can be either a remote or a local sort.
Ext.define('Ext.ux.form.field.FilterCombo', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.filtercombo',
/**
* #cfg {string} recordField
* #required
* The fieldname of the record that contains the filtervalue
*/
/**
* #cfg {string} searchField
* #required
* The fieldname on which the filter should be applied
*/
/**
* #cfg {boolean} clearable
* Indicates if the clear trigger should be hidden. Defaults to <tt>true</tt>.
*/
clearable: true,
initComponent: function () {
var me = this;
if (me.clearable)
me.trigger2Cls = 'x-form-clear-trigger';
else
delete me.onTrigger2Click;
me.addEvents(
/**
* #event clear
*
* #param {Ext.ux.form.field.FilterCombo} FilterCombo The filtercombo that triggered the event
*/
'clear',
/**
* #event beforefilter
*
* #param {Ext.ux.form.field.FilterCombo} FilterCombo The filtercombo that triggered the event
* #param {String/Number/Boolean/Float/Date} value The value to filter by
* #param {string} field The field to filter on
*/
'beforefilter'
);
me.callParent(arguments);
// fetch the id the save way
var ident = me.getId();
me.on('select', function (me, rec) {
var value = rec[0].data[me.recordField],
field = me.searchField;
me.fireEvent('beforefilter', me, value, field)
me.onShowClearTrigger(true);
me.onSearch(value, field);
}, me);
me.on('afterrender', function () { me.onShowClearTrigger(); }, me);
},
/**
* #abstract onSearch
* running a search on the store that may be removed separately
* #param {String/Number/Boolean/Float/Date} val The value to search for
* #param {String} field The name of the Field to search on
*/
onSearch: Ext.emptyFn,
/**
* #abstract onFilterRemove
* removing filters from the the
* #param {Boolean} silent Identifies if the filter should be removed without reloading the store
*/
onClear: Ext.emptyFn,
onShowClearTrigger: function (show) {
var me = this;
if (!me.clearable)
return;
show = (Ext.isBoolean(show)) ? show : false;
if (show) {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.setWidth(el.originWidth, false);
el.setVisible(true);
me.active = true;
}
});
} else {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.originWidth = el.getWidth();
el.setWidth(0, false);
el.setVisible(false);
me.active = false;
}
});
}
// Version specific methods
if (Ext.lastRegisteredVersion.shortVersion > 407) {
me.updateLayout();
} else {
me.updateEditState();
}
},
/**
* #override onTrigger2Click
* eventhandler
*/
onTrigger2Click: function (args) {
this.clear();
},
/**
* #private clear
* clears the current search
*/
clear: function () {
var me = this;
if (!me.clearable)
return;
me.onClear(false);
me.clearValue();
me.onShowClearTrigger(false);
me.fireEvent('clear', me);
}
});
Here is an untested implementation of your combo. Please note that I cleaned up your filterFn but I didn't make any further check.
{
xtype: 'filtercombo',
id: 'iccombo',
scope: this,
store: this.Combostore,
fieldLabel: 'Short State',
displayField: 'States',
valueField: 'States',
typeAhead: true,
triggerAction: 'all',
queryMode: 'remote',
name: 'State',
labelWidth: 125,
anchor: '95%',
// begin new parts
recordField: 'States',
searchField: '',
clearable: false,
onSearch: function (me, value, field) {
// New part!
var store = Ext.StoreMgr.lookup('YourStoreIdName');
// Clear existing filters
store.clearFilter(false);
// Build filter
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (item) {
var fieldNames = item.fields.keys,
fieldName, stringVal,
len = fieldNames.length,
j = 0;
for (; j < len; j++) {
fieldName = fieldNames[j];
if (item.data[fieldName] != null) {
stringVal = item.data[fieldName].toString();
if (stringVal != null && stringVal.toLowerCase().indexOf(value.toLowerCase()) != -1) {
return true;
}
}
}
return false;
}
});
// Apply filter to store
store.filter(myfilter);
}
}
I guess this should work too
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (rec) {
var fieldValue = rec[this.fieldName];
if (fieldValue && fieldValue === this.value)
return true;
return false;
}
});
I set this before two vars to mark them as from a external scope.
i see 2 issues
store should have remoteFilter: true set
in JavaScript all variables declarations are picked out and hoisted to the beginning of the function. so any variables declared inside a loop should be taken out and declared at the top of the function. JS has no block scope (like Java).

Resources