CQ/AEM extjs get selection dropdown box text, and get page path - extjs

Say I have a component with dialog drop to parsys on a content page /content/phonegap/ss/en_gb/login/home/test1/jcr:content/par/productimage
now inside the dialog i have something like
I wish to get $PATH append to URL and send selected device e.g the text 'Device ID:HTC_10_GOLD', to servlet in this dialog listener extjs:
<deviceAndColour
jcr:primaryType="cq:Widget"
allowBlank="{Boolean}false"
fieldLabel="Device and Colour"
name="./deviceAndColour"
options="/bin/reference/data/device.devices.json$PATH"
type="select"
xtype="selection">
<listeners
jcr:primaryType="nt:unstructured"
selectionchanged="function(pathfield) {
var selected = this.findParentByType('form').find('name', './deviceAndColour')[0].getText();
console.log( this.findParentByType('form').find('name', './deviceAndColour')[0].getText());
$.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json$PATH?selectedDevice=' + selected + '&colour=red', function(jsonData){
selectBox.setOptions(jsonData);
});
}" />
</deviceAndColour>
So bascially, the console.log( this.findParentByType('form').find('name', './deviceAndColour')[0].getText()); is not working as I expected, neither the $PATH inside the dialog listener js, it does not retrieve the path at all.
Apart from above attempt, I know var selected = this.findParentByType('form').find('name', './deviceAndColour')[0].getValue(); this will get the value asscociated with the select correctly, but I do not need value, I just wish to getText(), and get the current $PATH in extjs.
another questions, you may noticed $.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json$PATH?selectedDevice=' + selected + '&colour=red'
how do I skip the & in this listner, as if i use & directly, project won't even build. there must be someway to skip & and let extjs treat as part of the string to send request
Anyone expereinced this before? please suggest with code example.
Thanks

Getting the text of the option selected:
function(field,value){
for(var i = 0; i < field.options.length; i++) {
if(field.options[i].value === value) {
console.log('Selected: ' + field.options[i].text);
break;
}
}
}
Getting the path to the resource being edited:
function(field,value){
console.log("Resource:" + field.findParentByType("dialog").path);
}
Documentation: https://docs.adobe.com/docs/en/cq/5-6/widgets-api/index.html?class=CQ.form.Selection
UPDATE
Please try the following code adapted to your scenario (I also refactored the code to make use of params when providing query parameters. There is no reason why this shouldn't work.
function(field, value) {
var selected = '';
var path = field.findParentByType("dialog").path;
// get text of option selected
for(var i = 0; i < field.options.length; i++) {
if(field.options[i].value === value) {
selected = field.options[i].text;
break;
}
}
var params = {
selectedDevice: selected,
colour: 'red'
}
$.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json'+path, params, function(jsonData){
// FIXME: how are you getting the "selectBox" reference?
selectBox.setOptions(jsonData);
});
}

Related

AngularJS- how to return multiple buttons? Return a promise that resolves to an array?

I have an AngularJS application, and on one of the pages, there is a button that opens a dialog allowing the user to configure a particular widget. That widget displays a table, and I have recently added the functionality to allow the user to add a single button to a given table cell. This functionality all currently works correctly.
I am now trying to extend the functionality, to allow a user to add multiple buttons to a single table cell.
The way that the user currently adds a button to a cell, is by typing the address of the page that they want to add the button for into a text field on the dialog- the page that they type must be preceded with a : character. However, that text field is also used to display variable values, so for example, to display a variable value, they would type the name of the variable, i.e. myVariable, but to add a button which would link to the Home page, they would type :pages/home.
The value that they type into the text field is converted to the appropriate object for display in the table by a function called toItemObj(item).
This function is defined with:
function toItemObj() {
...
switch(widgetObj.name) {
case 'table':
...
widgetObj.table = angular.copy($scope.widget.table);
...
var multiBtns = ";";
var btnArray = [];
var btnTmplArray = [];
var pageTitle;
...
angular.forEach(widgetObj.table.rows, function(row) {
if(row.length > 0) {
reducedRows.push(row.map(
function(vrbl, idx) {
...
if(widgetObj.table.header[idx].startsWith(":")) {
if(vrbl.vrbl.includes(multiBtns)) {
btnArray = vrbl.vrbl.split(multiBtns);
if(btnArray[0].startsWith(":")) {
brnArray[0] = btnArray[0].split(":")[1];
}
var btnArrayBtn = 0;
angular.forEach(btnArray, function() {
var btnTmpl = $compile(
'<a href="javascript:;" ng-click="goToPage(target)"' +
'class="btn btn-xs btn-brand">Go to page</a>')
toVrblItemObj(btnArray[btnArrayBtn]);
compiledBtns.push(btnTmpl);
console.log("compiledBtns: ", compiledBtns);
})
} else {
console.log("Vrbl does not include multiBtns: ", vrbl.vrbl);
}
} else {
console.log("Value of widgetObj.table.headers[idx]: ", widgetObj.table.headers[idx]);
}
console.log("Returning vrbl.vrbl: ", vrbl.vrbl);
return vrbl.vrbl;
}
));
}
});
widgetObj.table.rows = reducedRows;
...
break;
In the if(vrbl.vrbl.includes(multiBtns)) { block, I am checking whether the value given to the button by the user contains a ; character, and if it does, I am splitting that string on the ; character, and compiling a button for each of the split elements, then pushing that compiled button to a list called compiledBtns.
The problem I'm having is with returning multiple buttons when they've been compiled...
When I load the page with this code as it is above, and try adding a button to a table cell, if I just add a single button to the cell (i.e. type something like :pages/page1 into the field in the dialog, then that button is displayed correctly. But, if I try to add more than one button to a table cell, (i.e. type something like :pages/page1;pages/page2 into the field in the dialog), then the compiled buttons are not displayed, and I just get the text that I actually typed:
I thought that this might be because my return statement is only returning the single vrbl.vrbl element (i.e. a single button), so I tried changing it to:
if(widgetObj.table.headers[idx].startsWith(":")) {
console.log("column heading starts with : - returning compiledBtns- ", compiledBtns);
return compiledBtns;
} else {
return tag.tag;
}
i.e. if the column is a 'buttons' column (the heading starts with a : character, then it should return the list of compiled buttons, but if not, then it should return a variable.
Doing this caused the table to be displayed as:
i.e. it stops even the cells with only one button from showing the button...
The console is showing me that the buttons are being compiled:
compiledBtns: (2) [ƒ, ƒ]0: ƒ publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn)1: ƒ publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn)length: 2__proto__: Array(0)
ctrl.js:467 Returning tag.tag: :pages/auth;pages/userpage1
but for some reason, they're just not being shown...
Can anyone explain what I'm doing wrong here? How can I get my function to return and display multiple buttons in the same table cell?
Edit
I've just noticed that when only entering one button in a table cell, the button is only displayed/ rendered as a button when you select it from the 'autocomplete' list that's displayed when you start typing- i.e. if you type out the page address in full, and click 'Preview' on the dialog, the button is not displayed in the table- it just shows the textual address that you typed.
When trying to add multiple buttons to the table cell, obviously the autocomplete doesn't recognise any pages with the address of two combined page address (separated with a ;), so you can't select more than one page from the drop down at a time.
How would I enable the selection of multiple items from the autocomplete drop down? The autocomplete functionality is defined in the HTML:
<div class="repeat-item-animation" data-ng-repeat="row in widget.table.rows">
<div class="row">
<div class="col-sm-12 input-addon-btn widget-picker-table-row-input">
<tags-input min-length="1"
um-max-tags-strict
key-property="tag"
data-ng-model="row"
display-property="tag"
template="tagItem.html"
replace-spaces-with-dashes="false"
on-tag-adding="onAddingTagItem($tag)"
on-tag-added="warning.rows = undefined"
um-tags-input-warning="{{warning.rows}}"
max-tags="{{widget.table.headers.length}}"
placeholder="Start typing a tag name or some text">
<auto-complete min-length="1"
load-on-focus="true"
load-on-empty="true"
display-property="tag"
select-first-match="false"
template="autocomplete.html"
source="autocompleteTagsFilter($query)">
</auto-complete>
</tags-input>
<a href data-ng-click="widget.table.rows.splice($index, 1)"
class="btn-icon btn-icon-sm btn-config btn-danger">
<span class="glyphicon glyphicon-minus-sign"></span>
</a>
</div>
</div>
I've Google'd a bit, but can't find any properties for the above <auto-complete> tag that would appear to enable selection of more than one element... Anyone have any suggestions?
Edit
So it seems that the auto-complete feature is actually a part of the implementation of ngTagsInput, which it seems the application is using to provide this functionality. From reading its documentation, it seems that the source parameter is the one I want to be using to return an array of strings (i.e. the links for multiple pages that I want to add buttons for). In the 'Parameters' table under the 'Usage' section of the autoComplete directive in ngTagsInput, the description of source is:
Expression to evaluate upon changing the input content. The input value is available as $query. The result of the expression must be a promise that eventually resolves to an array of strings.
In my application, the source attribute is currently set to the function autocompleteTagsFilter($query), which is defined with:
$scope.autocompleteTagsFilter = function(query) {
// Use last valid query keyword instead if input keyword is undefined
// When the first character of the tag that the user enters is a ':',
// the drop down should display a list of available user pages, not tags.
// Put the 'if(!query && lastTagQryKw)' inside the 'else', so that this
// is run whenever the entered tag name doesn't start with ':'
if (query.startsWith(":")) {
console.log("query starts with ':' ", query);
/*Check whether string has a ';' & split it if it does */
if(query.includes(";")) {
var strings = query.split(";");
console.log("strings[0]: ", strings[0]);
console.log("strings[1]: ", strings[1]);
var strings0 = strings[0].split(":")[1];
// console.log("strings0: ", strings0);
var strings1 = strings[1].split(":")[1];
// console.log("strings1: ", strings1);
/*Now need to pass each string to 'toTagItemObj(), to make it a tag item object', so that it can be displayed as a button */
angular.forEach(strings, function(string, btnString) {
// console.log("angular.forEach - value of string: ", string);
// console.log("angular.forEach - value of btnString: ", btnString);
toTagItemObj(string);
})
}
// Split the 'query' search string on ':', to use only the string
var buttonsQuery = query.substring(1);
if (!buttonsQuery && lastTagQryKw) {
buttonsQuery = lastTagQryKw;
}
/*check whether the buttonsQuery variable contains a ';' - if it does, split it */
if(buttonsQuery.includes(';')) {
console.log("buttonsQuery includes ; - ", buttonsQuery);
var btnsToDisplay = buttonsQuery.split(";");
console.log("btnsToDisplay: ", btnsToDisplay);
}
// Declare variables to be used in 'for' loop
var userPages = pagesPresets;
var page;
var result = [];
// 'For' loop should iterate through the list of user pages,
// and remove path, so that only the page name is shown
for (page in userPages) {
page = userPages[page];
// If the page key starts with 'buttonsQuery', and its length
// is greater than 6 (i.e. just 'pages/' shouldn't be displayed)
// add the page to the list of pages to be displayed.
if (page.key.startsWith(buttonsQuery) && page.key.length > 6) {
result.push(page.key);
}
};
if (result.length > 0) {
lastTagQryKw = query;
}
// Return the list of pages that match what the user has typed
return result;
// Otherwise, if the user types something that does not start with ':',
//then it should be a tag- search for tags that match this term
} else {
if (!query && lastTagQryKw) {
query = lastTagQryKw;
}
var result = Object.keys(fxTag.getTags()).filter(function(name) {
return name.indexOf(query.toLowerCase()) !== -1;
});
if (result.length > 0) {
lastTagQryKw = query;
}
return result;
}
};
As it currently stands, this autocompleteTagsFilter() function is returning an array of strings in its return statement. The array of strings is what's shown in the list of auto-complete options available, but the button is only actually created when selecting one of them.
According to the documentation, I need this to return a promise which resolves to an array of strings, but I'm not sure how I'd do this... Can anyone suggest a way I might make this function return a promise that resolves to an array of strings?
Edit
The template code for the markup/ cell rendering as it currently stands is:
angular.module('app.widget').run(function($templateCache) {
// Add basic suggestion template of autocomplete
$templateCache.put(
'autocomplete.html',
'<div class="pull-left autocomplete-text autocomplete-full-text">' +
'<span ng-bind-html="$highlight($getDisplayText())"></span></div>'
);
$templateCache.put('tagItem.html',
'<div data-ng-click="data.nounit = data.isTag && !data.nounit" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isTag"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-unit" ' +
'data-ng-hide="!data.units || data.nounit">({{data.units}})</span>' +
'<a class="remove-button" ng-click="$removeTag()">' +
String.fromCharCode(215) + '</a></div>'
);
}).controller(...){
I tried changing this to use an ng-if, so that it would show a different template for when buttons should be displayed for multiple pages, but this gives me an error in the console that says:
Error: [ng:areq] Argument 'WidgetPickerCtrl' is not a function, got undefined
I got this error after changing the template to:
angular.module('ultimetric.widget').run(function($templateCache) {
// Add basic suggestion template of autocomplete
$templateCache.put(
'autocomplete.html',
'<div class="pull-left autocomplete-text autocomplete-full-text">' +
'<span ng-bind-html="$highlight($getDisplayText())"></span></div>'
);
$templateCache.put('tagItem.html',
'<ng-if="data.isPages">' +
'<div data-ng-click="data.toPageBtn = data.isPages && !data.toPageBtn" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isBtn"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-text">' +
'data-ng-hide="!data.units || data.toPageBtn">({{data.unnits}})</span>' +
String.fromCharCode(215) + '</a></div>'
'<div data-ng-click="data.nounit = data.isTag && !data.nounit" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isTag"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-unit" ' +
'data-ng-hide="!data.units || data.nounit">({{data.units}})</span>' +
'<a class="remove-button" ng-click="$removeTag()">' +
String.fromCharCode(215) + '</a></div>'
);
}).controller(...){
Any ideas how I can get the autocomplete to show multiple buttons at the same time?

Excel-like behaviour of Grids in Ext JS

I'm trying to figure out a way to have an Excel-like behavior with the Grids on Ext JS.
Here is the sample grid I am working with. So far we can already naviguate through the cells with the arrows but only in edit mode.
However what I am trying to reach is the naviguation with the arrows, TAB and Enter keys outside of the edit mode, just like excel.
I tried to integrate this piece of code which overrides the Editor class, hoping that it would change the behavior of the cells but it doesn't change a thing.
I believe this is the most important part that overrides the Editor class and tries to include the keys input :
Ext.override(Ext.Editor, {
startEdit: function (el, value) {
var me = this,
field = me.field;
me.completeEdit();
me.boundEl = Ext.get(el);
value = Ext.isDefined(value) ? value : me.boundEl.dom.innerHTML;
if (!me.rendered) {
me.render(me.parentEl || document.body);
}
if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
me.startValue = value;
me.show();
field.reset();
if (deleteGridCellValue) {
field.setValue('');
me.editing = true;
me.completeEdit();
deleteGridCellValue = false; // reset global variable
}
else {
if (newGridCellValue == '') {
// default behaviour of Ext.Editor (see source if needed)
field.setValue(value);
}
else {
// custom behaviour to handle an alphanumeric key press from non-edit mode
field.setRawValue(newGridCellValue);
newGridCellValue = ''; // reset global variable
if (field instanceof Ext.form.field.ComboBox) {
// force the combo box's filtered dropdown list to be displayed (some browsers need this)
field.doQueryTask.delay(field.queryDelay);
}
}
me.realign(true);
field.focus(false, 10);
if (field.autoSize) {
field.autoSize();
}
me.editing = true;
}
}
}
});
This is the first time that I am working on a project that is outside of Comp-Sci classes so any help would be very much appreciated. Thanks !

working on select but not on blur - EXTJS

I am newbie to ExtJS I have the following lines of code that is working fine on select event and now I am planning to add on blur event too.
autoResolve.on("select" || "blur", function (component, record, index) {
var fieldSet = utils.getComponentFromMngr(component.id.split("~")[0]);
if(autoResolveData.CURRSEL){ //Set previous selection property
var xmlElem = fieldSet.DomainXML.documentElement.childNodes[1];
xmlElem.setAttribute("PR_DOMAINTYPE",autoResolveData.FILL_SUBTYP);
xmlElem.setAttribute("PR_DOMAINID", record.get("ITEMID"));
xmlElem.setAttribute("PR_DOMAINVALUE", record.data.TITLE);
fieldSet.DomainObj.push({PRDomainType:autoResolveData.FILL_SUBTYP,PRDomainID:record.get("ITEMID"),PRDomainValue:record.data.TITLE});
}
it is still working fine on select event but not on blur event where am I going wrong please suggest
"select" || "blur" will return select, as you can find out if you type the following in browser console:
console.log("select" || "blur");
Furthermore, "blur" event does not have record as the second parameter. You would have to look how to get record and call the function with a valid record parameter.
What you want to achieve is roughly the following:
var myFunction = function (component, record, index) {
var fieldSet = utils.getComponentFromMngr(component.id.split("~")[0]);
if(autoResolveData.CURRSEL){ //Set previous selection property
var xmlElem = fieldSet.DomainXML.documentElement.childNodes[1];
xmlElem.setAttribute("PR_DOMAINTYPE",autoResolveData.FILL_SUBTYP);
xmlElem.setAttribute("PR_DOMAINID", record.get("ITEMID"));
xmlElem.setAttribute("PR_DOMAINVALUE", record.data.TITLE);
fieldSet.DomainObj.push({PRDomainType:autoResolveData.FILL_SUBTYP,PRDomainID:record.get("ITEMID"),PRDomainValue:record.data.TITLE});
}
};
autoResolve.on({
select:myFunction,
blur:function(component) {
var record = ... // your special magic here
return myFunction(component,record);
}
});

How do I reset all filters in Extjs Grids?

How do I reset my ExtJS filters in my grids. More specifically, how do I get the header to honour the changes to the filtering.
ie. This works fine :
grid.store.clearFilter();
But the header rendering is all wrong. I need to get into all the menu objects and unselect the checkboxes.
I am getting pretty lost. I am pretty sure this gives me the filterItems :
var filterItems = grid.filters.filters.items;
And from each of these filter items, i can get to menu items like so :
var menuItems = filter.menu.items;
But that's as far as I can get. I am expecting some kind of checkbox object inside menu items, and then I can uncheck that checkbox, and hopefully the header rendering will then change.
UPDATE :
I now have this code. The grid store has its filter cleared. Next I get the filterItems from grid.filters.filters.items and iterate over them. Then I call a function on each of the menu items.
grid.store.clearFilter();
var filterItems = grid.filters.filters.items;
for (var i = 0; i<filterItems.length; i++){
var filter = filterItems[i];
filter.menu.items.each(function(checkbox) {
if (checkbox.setChecked)
checkbox.setChecked(false, true);
});
}
The checkboxes do get called, but still nothing is happening :(
Try this code:
grid.filters.clearFilters();
This should take care of both the grid and its underlying store.
When you do
grid.store.clearFilter();
it can only clear the filters on the store but the grid's view doesn't get updated with that call. Hence to handle it automatically for both the grid's view as well as the grid's store, just use
grid.filters.clearFilters();
Hope it helps!
Cheers!
Your update help me but you forget the case where you have input text instead of checkbox.
So this is my addition of your solution:
grid.filters.clearFilters();
var filterItems = grid.filters.filters.items;
for (var i = 0; i<filterItems.length; i++){
var filter = filterItems[i];
filter.menu.items.each(function(element) {
if (element.setChecked) {
element.setChecked(false, true);
}
if(typeof element.getValue !== "undefined" && element.getValue() !== "") {
element.setValue("");
}
});
}
When you use grid wiht gridfilters plugin
and inovoke
grid.filters.clearFilters();
it reset applyed filters, but it don't clean value in textfield inside menu.
For clean textfield text you can try this:
grid.filters.clearFilters();
const plugin = grid.getPlugin('gridfilters');
let activeFilter;
if('activeFilterMenuItem' in plugin) {
activeFilter = plugin.activeFilterMenuItem.activeFilter
}
if (activeFilter && activeFilter.type === "string") {
activeFilter.setValue("");
}

TinyMCE plug-ins not firing in Composite C1

I've created a new plug in as I could not find one that actually "works", hoping that if I do it from scratch it might fire.
The plug-in simply wraps selected text with a mailto: link.
I've added the plug-in to the includes file, as per the following response on a previous question: http://bit.ly/vGyQlE however, it's not working.
I've gone into the localization directory, identified the Composite.Web.VisualEditor.en-us.xml as the file that handles the localization, added my entry under :
<string key="ToolBar.ToolTipMailTo" value="Mail To" />
But when I hover of the "blank" block where the menu item should appear, it returns (?). This is the first part where I picked up on something wierd. When you actually click on where the item should appear, nothing happens. So, I can't assume that the click event has got to do with an image, I re-wrote the command to return an alert, when clicked:
tinymce.create('tinymce.plugins.MailTo', {
init : function(ed, url) {
ed.addButton('mailto', {
title : 'mailto.mailto_desc',
cmd : 'mceMailTo',
image : url + '/images/mailto.gif'
});
ed.addCommand('mceMailTo', function() {
var selectedText = ed.selection.getContent({format : 'text'});
var MailToLink = "alert(" + selectedText + ");";
ed.execCommand('mceInsertContent', false, MailToLink);
});
I've added the "mailTo" element to visualeditor.js:
plugins : "...,paste,lists,mailto",
And ensured that the "mailto" plug-in is situated under the plug-ins directory for tiny_mce. I've gone as far as to clear my cache several times, but nothing? Can it be this difficult to add new plug-ins to tiny-mce in Composite?
1) Composite C1 does not support internal tiny_mce buttons
Do you add button to editor?
In file Composite\content\misc\editors\visualeditor\includes\toolbarsimple.inc add
<ui:toolbargroup>
<ui:toolbarbutton cmd="mceMailTo" tooltip="Mail to" image="${icon:paste}" isdisabled="false" />
</ui:toolbargroup>
2) Do you write valid plugin code?
(function () {
tinymce.create('tinymce.plugins.MailTo', {
init: function (ed, url) {
ed.addCommand('mceMailTo', function () {
var selectedText = ed.selection.getContent({ format: 'text' });
var MailToLink = "alert(" + selectedText + ");";
ed.execCommand('mceInsertContent', false, MailToLink);
});
}
});
tinymce.PluginManager.add('mailto', tinymce.plugins.MailTo); })();

Resources