Goal
Limit number of components added in a parsys, where the limit is configurable in design dialog
For a different implementation (configure component limit in each page) - check this post
For TouchUI check this post
Demo | Package Install
Parsys Limit - Design Mode
Stored in CRX
Limit Exceeded - Drag and Drop
Limit Exceeded - New menu item disabled
Solution
1) Login to CRXDE Lite, create folder (nt:folder) /apps/classicui-design-configurable-limit-parsys
2) Create clientlib (type cq:ClientLibraryFolder) /apps/classicui-design-configurable-limit-parsys/clientlib and set property categories of String type to cq.widgets and dependencies to underscore
3) Create file ( type nt:file ) /apps/classicui-design-configurable-limit-parsys/clientlib/js.txt, add the following
limit-parsys.js
4) Create file ( type nt:file ) /apps/classicui-design-configurable-limit-parsys/clientlib/limit-parsys.js, add the following code
(function(){
var PARSYS_DESIGN_DIALOG = "/libs/foundation/components/parsys/design_dialog",
PARSYS_LIMIT = "eaemComponentLimit";
CQ.Ext.onReady(function () {
if(CQ.WCM.isEditMode()){
handleEditMode();
}
if(CQ.WCM.isDesignMode()){
handleDesignMode();
}
});
function handleDesignMode(){
extendShowDialog();
}
function handleEditMode(){
CQ.WCM.on("editablesready", applyLimitAndExtendDrop, this);
}
function getSiblings(editable){
var parent, siblings = [];
_.each(CQ.WCM.getEditables(), function(e){
parent = e.getParent();
if(!parent || (parent.path !== editable.getParent().path)){
return;
}
siblings.push(e);
});
return siblings;
}
function isWithinLimit(editComponent){
var pageInfo = CQ.utils.WCM.getPageInfo(editComponent.path),
isWithin = true, currentLimit = "",
cellSearchPath, parentPath, parName;
if(!pageInfo || !pageInfo.designObject){
return;
}
try{
cellSearchPath = editComponent.cellSearchPath;
parentPath = editComponent.getParent().path;
cellSearchPath = cellSearchPath.substring(0, cellSearchPath.indexOf("|"));
parName = parentPath.substring(parentPath.lastIndexOf("/") + 1);
currentLimit = pageInfo.designObject.content[cellSearchPath][parName][PARSYS_LIMIT];
if(currentLimit){
isWithin = getSiblings(editComponent).length <= parseInt(currentLimit);
}
}catch(err){
console.log("Experience AEM - Error getting the component limit", err);
}
return {
isWithin: isWithin,
currentLimit: currentLimit
};
}
function extendDrop(dropFn){
return function(dragSource, e, data){
var limit = isWithinLimit(this.editComponent);
if(!limit.isWithin){
this.editComponent.hideTarget();
CQ.Ext.Msg.alert('Error', "Limit exceeded, allowed - " + limit.currentLimit);
return false;
}
return dropFn.call(this, dragSource, e, data);
};
}
function disableNewMenuItem(){
var component = this.element.linkedEditComponent;
if (!component || !component.menuComponent) {
return;
}
var menu = component.menuComponent;
if (menu.eaemLimitSet) {
return;
}
menu.eaemLimitSet = true;
var limit = isWithinLimit(this);
var newItem = menu.findBy(function(comp){
return comp.text === "New...";
})[0];
if(!limit.isWithin){
newItem.setDisabled(true);
}
}
function isParsysNew(editable){
return _.isObject(editable.params)
&& (editable.params["./sling:resourceType"] == CQ.wcm.EditBase.PARSYS_NEW);
}
function applyLimitAndExtendDrop() {
var editables = CQ.utils.WCM.getEditables();
_.each(editables, function (editable) {
if (isParsysNew(editable)) {
editable.emptyComponent.el.on("contextmenu", disableNewMenuItem, editable);
}
var dropTargets = editable.getDropTargets();
if(_.isEmpty(dropTargets)){
return;
}
dropTargets[0].notifyDrop = extendDrop(dropTargets[0].notifyDrop);
});
}
function extendShowDialog(){
CQ.wcm.EditBase.showDialog = (function(showDialogFn) {
return function(editComponent, type, ignoreIsContainer){
if(editComponent.dialog !== PARSYS_DESIGN_DIALOG){
return;
}
var isFirstRun = !editComponent.dialogs[CQ.wcm.EditBase.EDIT];
showDialogFn.call(this, editComponent, type, ignoreIsContainer);
if(isFirstRun){
editComponent.getEditDialog().on("loadcontent", extendParsysDialog);
}
}
}(CQ.wcm.EditBase.showDialog));
}
function extendParsysDialog(dialog){
$.ajax( dialog.path + ".2.json" ).done(handler);
function handler(data){
dialog.un("loadcontent", extendParsysDialog);
var limitField = new CQ.Ext.form.TextField({
value: data[PARSYS_LIMIT] || "",
fieldLabel: "Component Limit",
name: "./" + PARSYS_LIMIT,
style: {
marginBottom: '10px'
}
});
var compSelector = dialog.findByType("componentselector")[0],
ownerCt = compSelector.ownerCt;
ownerCt.insert(2, limitField);
ownerCt.doLayout();
}
}
}());