Dependent Menu-Field

Dependent Menu-Field

by Nils Ratnaweera -
Number of replies: 21

Hello everyone,

I'm not even sure if there is a precise word for this, but let me explain what I'm looking for:

I'd like to have two "menu" - fields in a moodle Database activity. Field 1 acts as a preselection for what elements the user can choose from in field 2.

For Example if Field 1 is are the continents and the user chooses "Europe", the selection in Field 2 "Country" should be reduced to the countries within Europe.

Is there such a possibilty of "dependant menu fields" in a Moodle Database activity? Thanks in advance for any advice,

Nils

Average of ratings: -
In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Hi Nils,

I don't think the current version of Database Mod can do the Dependency Menu.

What are you going to do? If you just want to collect user information, you can use Feedback module, it is very easy to set up and works very well. 

https://docs.moodle.org/28/en/Building_Feedback#Dependence_item_and_Dependence_value

In reply to William Lu

Re: Dependent Menu-Field

by Nils Ratnaweera -
Hi Wiliam,

Thanks for your response! This is kind of what I feared...

Actually, I'm using the database activity to enable course particpants to record their monitary and time expenditures. There are about 70 different accounts a certain expenditure can be assigned to, beloning to about 4 different groups. At the moment, the user has to choose from the 70 accounts, which results in a huge, user-unfriendly dropdown list. It would be much more elegant if the user could first choose one of the 4 dfferent categories and then from the according subcategories..

Is there another way to solve this problem?
In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

If not every one needs to access 70 accounts, you can use the 'Grouping access' feature to separate your users to see a different database activity, with only the accounts they need in each. Of course, you, as a teacher, has to manage 4 Database activities. Sorry, I don't think this is a good idea. Hope Itamar can use his magic Javascript to do a trick for you smile



In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by Robert Brenstein -
One workabout could be that instead of having a single menu with 70 items, you have 4 menus, one for each category. Knowing the category, users choose menu applicable in a given case. Good structure of the form with appropriate labelling should do the trick.
Average of ratings: Useful (1)
In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

I tried to use JavaScript to achieve that. Please find attached preset, it is working now.

menu

I am still learning, hope Itamar can review my script and make it works more efficient.

<script type="text/javascript">
document.getElementById("menu1").style.display = 'none';
document.getElementById("menu2").style.display = 'none';
document.getElementById("menu3").style.display = 'none';
document.getElementById("more").style.display = 'none';
var menuselection=document.getElementById('menu#id');
document.getElementById('menu#id').onchange=function(){display();};
function display() {
if ( menuselection.selectedIndex==1 ) {
document.getElementById("more").style.display = 'block';
document.getElementById("menu1").style.display = 'block';
document.getElementById("menu2").style.display = 'none';
document.getElementById("menu3").style.display = 'none';
}

if ( menuselection.selectedIndex==2 ) {
document.getElementById("more").style.display = 'block';
document.getElementById("menu1").style.display = 'none';
document.getElementById("menu2").style.display = 'block';
document.getElementById("menu3").style.display = 'none';
}
if ( menuselection.selectedIndex==3 ) {
document.getElementById("more").style.display = 'block';
document.getElementById("menu1").style.display = 'none';
document.getElementById("menu2").style.display = 'none';
document.getElementById("menu3").style.display = 'block';
}
}
</script>
Average of ratings: Useful (2)
In reply to William Lu

Re: Dependent Menu-Field

by Nils Ratnaweera -

Hey William,

I'm speechless!! Thank you sooo much for tailoring a script to fit my needs exactly! Now the only problem is.. I've never implemented JavaScript in Moodle before.. Any guides/wikilinks that you can point out?

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Hi Nils,

Glad to know it is useful. (Forgot to Rate... useful? smile)

I've included the scripts in the preset file. Just download it and import it in the 'Presets' tab.

All you have to do is to edit your menu fields with 4 areas' name and 70 countries' name. (You can change the menu1,2,3,4...but, you don't have to).

If you do want to edit the template (The script is hidden in 'Add template') make sure you have clicked on the 'Disable editor' button first.

You can also move all the scripts into the 'Javascript template' tab, then you don't need to 'Disable editor' each time.

In reply to William Lu

Re: Dependent Menu-Field

by Nils Ratnaweera -

Hey William,

So, I was off for a couple of days.. flu an stuff. I rated your post, definitly "useful" smile

I'll definitly have to adapt the script to my needs.. but for this I'll have to read into javascript in order to better understand what you've produced. This may take a while, I'll report back once I'm done!

In reply to William Lu

Re: Dependent Menu-Field

by Itamar Tzadok -

You can simplify a bit the javascript by defining the dependencies in an array and then iterating the array as needed. for example:

var submenus = ['more', 'menu1', 'menu2', 'menu3'];
var mainmenu = [
[],
['more', 'menu1'],
['more', 'menu2'],
['more', 'menu3']
];
function showHideSubmenus(mainmenu, submenus, index) {
for (var h in submenus) {
// hide element submenu[h].
}
for (var s in mainmenu[index]) {
// show element mainmenu[index][s].
}
}

hth smile

Average of ratings: Useful (1)
In reply to Itamar Tzadok

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Thank you very much Itamar, I knew you can make the script more professional. 

Just tested, but not sure how to use the 'h' and 's'. Should I remove the two '//' as well?

In reply to William Lu

Re: Dependent Menu-Field

by Itamar Tzadok -

It was really just a stub. A working example could look something like the following.

In the JS tab:

var submenus = ['cars', 'planes', 'ships'];
var menudependencies = [
[],
['cars'],
['planes'],
['planes', 'ships']
];

function showHideSubmenus(index) {
// Hide the sub menus.
for (var h in submenus) {
document.getElementById(submenus[h]).style.display = 'none';
}

// Show the selected sub menus.
for (var s in menudependencies[index]) {
document.getElementById(menudependencies[index][s]).style.display = 'block';
}
}

The submenus is a list of element ids of div wrappers for the fields in the add template. The idea is to wrap the field pattern with an identified container element. In the template it would be something like: <div id="cars">[ [Cars] ]</div>.  Then for hide/show we don't need to worry about the actual id of the field element and simply hide/show the wrapper. This also allow us to put most of the javascript in the JS tab rather than in the template.

The list of menu dependencies simply defines for each index of the main menu which sub menus should be displayed.

The showHideSubmenus is the event handler. It first hides all the submenus and then shows only those which are defined in the dependencies for the given index.

All the sub menus are hidden by default via css in the CSS tab.

#cars, #planes, #ships { display: none; }

In the add template we assign the event handler to the onchange event of the main menu. We also call the eventhandler directly so that an existing selection would be applied when opening the entry for editing.

<script>
// Set the onchange event of the category menu.
var category = document.getElementById('Categories#id');
category.onchange = function() {showHideSubmenus(this.selectedIndex);};
showHideSubmenus(category.selectedIndex);
</script>


The approach hiding/showing wrappers rather than specific fields can have an interesting application. If you have an entry with many fields that are organized in sections, you can use a menu field to show one section at a time, and make the entering of content more manageable.


smile

Average of ratings: Useful (2)
In reply to Itamar Tzadok

Re: Dependent Menu-Field

by Nils Ratnaweera -

Hey Itamar,

Cool stuff, thanks for developping the script a giving a working example! Unfortunately, I can't seem to get the working example to work... This has most certainly to do with my lack of JS and moodle Database knowledge. I do have some programming experiance in other languages (Python/R) and I'm determined to adapt and implement your script in my moodlecourse.

So under "Templates" I added the first script to the "Javascript template" tab, the second to the "CSS template" Tab and the thrid to the "Add template" Tab.  What is the next step? Must I add the fields via the "Fields" Tab and set the wrappers accordingly? Where do I set the "Field ids"?

I hope you can help me on what I hope is the last Step to understand the system!

Nils

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by Itamar Tzadok -

Yes, you need to add fields. For this example you can add 4 menu fields:

Categories

The example assumes 3 options. the default 'Choose' will display none of the sub menus. The first options will display the Cars menu. The second option will display the Planes manu. The third will display the Planes and Ships menus. This is defined in the menudependcies array in the JS tab and you can change that definition to display the menus differently as desired. Note that that you can put anything you want in the option labels.

Cars

With some options.

Planes

With some options.

Ships

With some options.


The add template can look something like the following:

<div id="addtemplate">
<div id="category">Categories: [ [Categories]]</div>
<div id="cars">Cars: [ [Cars]]</div>
<div id="planes">Planes: [ [Planes]]</div>
<div id="ships">Ships: [ [Ships]]</div>
</div>

<script>
   // Set the onchange event of the category menu.
   var category = document.getElementById('[ [Categories#id]]');
   category.onchange = function() {showHideSubmenus(this.selectedIndex);};
   showHideSubmenus(category.selectedIndex);
</script>

(If you copy this code you need to remove the redundant spaces between square brackets)

hth smile

Average of ratings: Useful (2)
In reply to Itamar Tzadok

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Dear Itamar,

As usual, your JavaScript is a magic and your lesson is detailed, and your suggestion is inspired. I followed your guide and put more stuff into the Wrappers, like Text input; radio buttons; images and wordy explanations; all are wrapped up perfectly by the main menu. 

Many many thanks to you.

In reply to Itamar Tzadok

Re: Dependent Menu-Field

by Nils Ratnaweera -

Dear Itmar,

I've withheld an answer to you last, very useful post, because I wanted to wait till I've finally implemented your scrip. I've been trying to start from your example and develop it so that it fits my need, but I kept getting stuck on the first step: to remove the appearance of  the submenus "ships" AND "planes" submenufields when choosing just "ships" in the mainmenu.

You, and logic, told me in order to do this I would just have to remove the entry " 'planes' " in the JS tab. Strangely, this didn't seep to work. I tried and tried and was beginning to despair when I found the core of the problem:

When I take your script, save the template, test it, then adjust the script, the modification is not applied! If I modify the script before I save it the first time, the modification is implemented and the script runs the way I would expect it.

Now this issue is actually not related to my original question, but can you point out the solution to this issue none the less? Is this a known issue?

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by Itamar Tzadok -

The change in the js should be

var submenus = ['cars', 'planes', 'ships'];
var menudependencies = [
[],
['cars'],
['planes'],
['ships']
];

In the add entry template code make sure to remove redundant spaces between the square brackets of the field patterns, for example,

[ [Categor

should be changed to

[[Categor

hth smile

In reply to Itamar Tzadok

Re: Dependent Menu-Field

by Nils Ratnaweera -

Hey Itmar,

Thanks for your response, but I think you missed my point a little bit. The point was that modifications to my JavaScript in the JS-Tab have no effect on the behaviour of the Database. In other words, if I use your initial JS, save it, then modify it (removing "planes"), the "planes" subcategory is still revealed when using option 3.

Only If I DON'T save the JS template before modifying it, only AFTER modifying it, the Database works as I would want it.

I feel as if I'm not expressing myself clearly.. do you get what I mean though?

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by Itamar Tzadok -

You may need to clear caches in the browser level and maybe also Moodle caches for changes to take effect. If the javascript doesn't work at all after modification, you can use the browser's developer web console to debug it. smile

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Hi Nils,

As Itamar said, after you changed something in JS or in CSS each time , press "Crtl+R"  to see a refreshed page.

In reply to Nils Ratnaweera

Re: Dependent Menu-Field

by William Lu -
Picture of Particularly helpful Moodlers

Please download the guide and presets for Dependent Menu from: https://moodle.net/mod/data/view.php?d=1&rid=141


In reply to William Lu

Re: Dependent Menu-Field

by Nils Ratnaweera -

Dear Itamar, and dear William,

I know this is very late.. but thank you guys so much for spoonfeeding me through my little problem. It works now and the thing is pretty neat!