How to create cascading drop down in alfresco - alfresco

I have requirement where I have to create a cascading dropdown, means the value of the second dropdown will depends on the first one. The list are going to be hardcoded.
For e.g. The first dropdown of country and based on this value the second dropdown is of state corresponding to that country; I will have the list of state corresponding to each country.
Now I know how to apply constraint on the field using list but can that be extended to two level ??
I have also searched about datalist but I really don't want that as the list is going to be pan customer but datalist will make it customer specific and don't want customer to create the list themselves.
Any help will be highly appreciated.
Thanks

Alfresco does not have cascading dropdown lists out of the box.
Nevertheless, it will not be difficult to develop one.
Because your lists are hardcoded, there is a simple solution.
Generate the controls for the two dropdown lists. They can even be just an hidden field or an empty dropdown. If you are not familiar with the Alfresco form engine, it will be probably easier to use an hidden field like in the following example (snippet of the configuration in share-config-custom.xml):
<field id="dropdown1">
<control template="/org/alfresco/components/form/controls/hidden.ftl">
<control-param name="contextProperty">dropdown1</control-param>
</control>
</field>
<field id="dropdown2">
<control template="/org/alfresco/components/form/controls/hidden.ftl">
<control-param name="contextProperty">dropdown2</control-param>
</control>
</field>
In share-config-custom.xml, you can also add a new custom javascript and css:
<!-- Document Library client-side dependencies -->
<config evaluator="string-compare" condition="DocLibCustom">
<dependencies>
<js src="/js/cascading-dropdown.js" />
<css src="/css/cascading-dropdown.css" />
</dependencies>
</config>
The custom javascript and css can, using YUI, find the two controls and generate the necessary dropdown and event listeners. When the dropdown is updated, an event listener will update the cascading dropdown and will also update the hidden fields. The value of the hidden fields will be posted when the form is closed.
A better solution can be to use the custom javascript only to define a javascript library. You can then use this javascript library in a custom free marker template to use instead of "/org/alfresco/components/form/controls/hidden.ftl"
You can find hidden.ftl in:
/webapps/share/WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/form/controls/hidden.ftl
Do a copy of this ftl in the extension path and modify it to use your custom javascript.
If the lists are not hardcoded, simply use a webscript to generate the list and call it from the browser using ajax calls.

As you are with alfresco community edition 5.0.d, you can/should be using aikau for that sort of things, you can start learning and using aikau based on this tutorial for standalone aikau clients or this tutorial for new custom share pages.
Once you have your page setup (desc.xml, js and ftl), you should have something like this:
custom-page.get.desc.xml
<webscript>
<shortname>Test page 1</shortname>
<family>testing</family>
<url>/custom-uri</url>
</webscript>
custom-page.get.ftl
<#processJsonModel group="share"/>
and custom-page.get.js
model.jsonModel = {
services : [ "alfresco/services/NavigationService",
"alfresco/services/LogoutService",
// Add more services here !!!
],
widgets : [
// Add more widgets here
]
};
You need now to add two selects to your widgets array:
{
name : "alfresco/forms/Form",
config : {
scopeFormControls : false, // to avoid complex handling of
// pubSubScope
widgets : [ {
name : "alfresco/forms/controls/Select",
config : {
fieldId : "LEVEL_1",
label : "Level 1",
description : "Select an item from the list",
name : "level_1",
value : "1",
requirementConfig : {
initialValue : true
},
optionsConfig : {
fixed : [ {
label : "Item 1",
value : "1"
}, {
label : "Item 2",
value : "2"
}, {
label : "Item 3",
value : "3"
} ]
}
}
}, {
name : "alfresco/forms/controls/Select",
id : "LEVEL_2",
config : {
fieldId : "LEVEL_2",
label : "Level 2",
description : "Select an item from the list",
name : "level_2",
requirementConfig : {
initialValue : true
},
optionsConfig : {
fixed : [ {
label : "Item 1.1",
value : "1"
}, {
label : "Item 1.2",
value : "2"
}, {
label : "Item 1.3",
value : "3"
} ]
}
}
} ]
}
}
The next step would be to detect the change event in your first select then refresh the list of the second select with new items. In order to do so, you need either to define a new service or extend the select widget for your second select. I will go with option number 1:
CustomServiceForNestedSelects.js
define(
[ "dojo/_base/declare", "alfresco/core/Core", "dojo/_base/lang",
"alfresco/core/CoreXhr", "service/constants/Default" ],
function(declare, Core, lang, CoreXhr, AlfConstants) {
return declare(
[ Core, CoreXhr ],
{
pubSubScope : "",
levelOneFieldId : "LEVEL_1",
levelTwoFieldId : "LEVEL_2",
levelTwoItems : [ [ {
label : "Item 1.1",
value : "1"
}, {
label : "Item 1.2",
value : "2"
}, {
label : "Item 1.3",
value : "3"
} ], [ {
label : "Item 2.1",
value : "1"
}, {
label : "Item 2.2",
value : "2"
}, {
label : "Item 2.3",
value : "3"
} ], [ {
label : "Item 3.1",
value : "1"
}, {
label : "Item 3.2",
value : "2"
}, {
label : "Item 3.3",
value : "3"
} ] ],
constructor : function yreg_CustomServiceForNestedSelects__constructor(
args) {
lang.mixin(this, args);
this.alfSubscribe(this.pubSubScope
+ "_valueChangeOf_" + this.levelOneFieldId,
lang.hitch(this, this.levelOneChanged));
},
levelOneChanged : function yreg_CustomServiceForNestedSelects__levelOneChanged(
payload) {
var value = payload.value;
var levelTwo = dijit.byId(this.levelTwoFieldId);
if (levelTwo)
levelTwo
.setOptions(this.levelTwoItems[value - 1]);
},
});
});
And now, all that is left is to include your service in your page model
"<custom-package>/CustomServiceForNestedSelects"
Note:
You will need to register a custom package and place your service there, in order to learn how to do so, check this tutorial.
To test your page's content check http://127.0.0.1:8090/aikau-sample/page/na/ws/custom-uri in case you are trying this code in a stand alone aikau app, or http://127.0.0.1:8080/share/page/dp/ws/custom-uri, http://127.0.0.1:8080/share/page/hdp/ws/custom-uri for share custom pages

Aikau does actually provide cascading menus out-of-the-box, see the alfresco/menus/AlfCascadingMenu widget. There are examples of its use already present within Alfresco (for example the favourites list from under the sites menu in the main header in Share). If you already know the cascading contents then this can all be defined in your page WebScript. However, if you need to dynamically change the content of the cascaded menu then you should look at the implementation of alfresco/header/AlfSitesMenu or alfresco/documentlibrary/AlfCreateTemplateContentMenu (you can find the source in the Aikau GitHub project).

Some useful info: https://forums.alfresco.com/forum/developer-discussions/alfresco-share-development/cascading-dropdown-alfresco-share-03102011
Used that as a basis for my own cascading selects. Builds on the same concept as described by Marco Altieri

Related

Storybook: How to have a "select" control with options but also free text?

In storybook (React), I have set-up the following control that correctly displays iconClass as a select. I want it to provide those three "most used" options, but also give the user the ability to input a custom value. How to get this hybrid select / input text control? Thanks
argTypes: {
iconClass: {
control: {
type: 'select',
options: [
'fa-arrows-alt-v',
'fa-arrows-alt-h',
'fa-link',
]
},
}
}

How to make ReferenceManyField to match childs from a url?

I'm building an application with react-admin and Spring REST as data API. I'm in the typical situation of having a Product object with relative comments.
In the element that shows the product I added ReferenceManyField in order to show the comments for that product. But here is the problem: according to the reference documentation, this component fetches the comments calling the comments list and finding comments that have the id of the father:
https://marmelab.com/react-admin/Fields.html#referencemanyfield
But since my Spring REST produces something similar for one specific product:
{
"id" : 1,
"description" : "Description of Product 1",
"price" : 50045.0,
"title" : "Title of product 1",
"additionaldata" : [ ],
"pimages" : [ ],
"_links" : {
"self" : {
"href" : "http://localhost:8080/products/1"
},
"product" : {
"href" : "http://localhost:8080/products/1"
},
"pcomments" : {
"href" : "http://localhost:8080/products/1/pcomments"
},
"vendor" : {
"href" : "http://localhost:8080/products/1/vendor"
}
}
}
The logic is inverse, we find the URI to call to find the list of comments for that particular product.
How can I make ReferenceManyField to call http://localhost:8080/products/1/pcomments to fetch comments for that product?
Try to do the following:
<ReferenceManyField source="your_source" target="_links['pcomments']['href']" reference="products"/>
You can wrap your search url in the filter prop of ReferenceManyField, then in your dataProvider get the filter from GET_MANY_REFERENCE(switch case). then you can do whatever you want

How to add newly created values in grid in extjs4

I am working in extjs4. I have gridview as-
{
xtype : 'grid',
id : 'g2',
store : 'qb.qbquestionoptionStore',
columns : [ {
text : 'Options',
dataIndex : 'option',
flex : 1
}, {
text : 'Answer',
dataIndex : 'isAnswer',
flex : 2.5
},{
header : 'edit',
renderer : function(val) {
return 'Edit';
}
},
Above grid is showing option and isAnswer field. I am also having addoption button. When i am clicking on this button,its showing new option creation window as-
on click of its save button i want to add thses new option and isAnswer fields value in above grid.I have retrived thses newly inserted field values by code-
var win = button.up('window');
form = win.down('form');
record = form.getRecord(), values = form.getValues();
console.log(values.option);
console.log(values.isAnswer);
So how to insert these values in above grid?
There is separation between data and view in ExtJs. All data related stuff is handled via stores and models. So if you want to insert new row into the grid you should insert it into the corresponding store:
var store = Ext.getStore('qb.qbquestionoptionStore');
store.add(form.getValues());

CKEditor.net table class

In a asp.net C# webapp I'm using the CKEditor 3.6.2 and I'm facing the following problem:
In my stylesheet I have a CSS class to use in tables and I'm trying to bring this class already filled in the "Table properties", "Advanced" tab and the "Stylesheet Classes" field.
I want to bring this field filled with the string "blue_table", which is the name of my CSS class. I'm working with the source of the "table" plugin. I have figured out how to change the value of fields like width and height, but the one I want is the "Stylesheet Classes" field.
Do any of you know to to set a default value for this field?
You don't have to edit the ckeditor.js file to customise the editor. You can add the following either to config.js and use it site wide or on any page where you're using CKEditor (inside a script tag as below, after the editor fields you're using).
<script type="text/javascript">
CKEDITOR.on( 'dialogDefinition', function( ev ) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested on (the Table dialog).
if ( dialogName == 'table' ) {
// Set the tab
var advTab = dialogDefinition.getContents( 'advanced');
// Grab the field
var stylesField = advTab.get('advCSSClasses');
// Set the default value
stylesField['default'] = 'blue_table';
}
});
</script>
This is modified from the CKEditor documentation. The hardest part is working out the IDs and names for all the fields used in the dialogs.
Finally I found the answer. This property is in the dialogadvtab, in the property "advCSSClasses". The thing is that this plugin is inside the core js, I mean the ckeditor.js.
I had to do this :
children :
[
{
id : 'advCSSClasses',
att : 'class',
type : 'text',
label : lang.cssClasses,
'default' : 'blue_table',
setup : setupAdvParams,
commit : commitAdvParams
}
]
The "problem" now is that I had to do it in the ckeditor.js, which is not a good practice. The problem is solved, but not int the best way.

jsTree async with preloaded data

I am trying to make a tree view be async. When the page is rendered, there is default tree items displayed. jsTree tries to reload the root anyway.
I want the page to render (with jsTree init'ed) with default items rendered from browser, not the ajax call. Then we the user tries to go deeper, thats when I want to do do the ajax calls.
Any help is appreciated. Thanks!
From Documentation: If both data and ajax are set the initial tree is rendered from the data string. When opening a closed node (that has no loaded children) an AJAX request is made.
An example,
$(function () {
$("#demo4").jstree({
"json_data" : {
"data" : [
{
"data" : "A node",
"state" : "closed"
},
{
"attr" : { "id" : "li.node.id" },
"data" : {
"title" : "Long format demo",
"attr" : { "href" : "#" }
}
}
],
"ajax" : { "url" : "/static/v.1.0rc/_docs/_json_data.json" }
},
"plugins" : [ "themes", "json_data" ]
});
});

Resources