Skip to content

6. more advanced customization to make the UI work for the user

sam detweiler edited this page Dec 17, 2024 · 2 revisions

we need to hide the field(s) if not used by the selection

so css helps here , we'll hide the 1st div under the fieldset for this modules form contents (the developers window can show the html layout of the generated form)

.m_compliments fieldset div[class$="format"] {
  display:none;
}

this is really a module specific css.. hm.. how to add to the form page?

now the custom fields are hidden.. oops.. IF the fields WERE set, then the selection list (when) should be set to 'date-format' AND the field with the actual data should be shown WHEN the form is opened..

hmm, can't do that without code.. (css doesn't have a field contents operator yet)

this is really module specific js code.. hm.. how to add to the form page? AND how does it get invoked..

for both these problems, I have extended MMM-Config to support 2 new files in the module folder

MMM-Config.extension.css
MMM-Config.extension.js

the form builder will locate and add these to the html file used to launch the config form. and will do the same for any module with an extension..

ok, now have the fields hidden....

but we need to expose ONE choice when the form is loaded.. we have the active choice on the onChange() handler.

the DOM model documents a 'ready' event when the document is loaded and ready to be viewed

but we cant use the browser document.on('ready') event , because this happens WAY before the form is built..

turns out one can make custom events.., so I create the 'form_loaded' event

so after the form is generated into the web page, the event 'form_loaded' is fired,

so for compliments a little event handler in MMM-Config extension.js can process when the form is loaded.. here JQuery makes quick work

  • find all the elements in the document, wherever they are, that are the selected option of the select list * in the m_compliments (note that we added this class via the htmlClass JSONFORAM option) document tree with a classname specfied that ends with '---when' (that json form generated, from our property name ('when') ) and then LOOP thru those

    • if the selected option ends with '-format', its one of the special types, and we need to surface the extra input field (change its display style setting)

      • so, look back UP the document tree for the first fieldset element (see the generated html)

      • then find (downward) the div with a class name that ends with the text of the selection entry (JSON form generated from our fieldHtmlClass value)

      • and set its display attribute to not none to make it visible again (in this case display:block works)

the cool part of JQuery here is that this one 'search' will return ALL instances of this selected option across as many compliments form elements in the entire doc, across multiple instances, etc.. no special coding required.

// on form_loaded event
$(document).on('form_loaded', function () {
	// find all the elements of our when selection list and get the selected option in each
	$('.m_compliments div[class$="---when"]  option:selected').each(
		// process each 
		function(){
			// get its selected option text
			var selected_option_value=$(this).val(); //.text() contains the visible value from titlemap, .val() contains the enum value
													 // if no title map .text() and .val() are the same
			// if its one of the special fields 
			if(selected_option_value.endsWith('-format')){
				// look above the select to the previous element that encloses select and the custom fields (fieldset) 
				$(this).closest('fieldset')
					// find below the fieldset to find the appropriate div with the right class, 
					.find('div[class$="'+selected_option_value+'"]')
						// and set its display style property to block, previously set to display:none by MMM-Config.extension.css
						.css('display','block')
			}
		}
	)
})

so, we have our custom fields, the form loader will put the right data in the fields(schema and form), they all will be hidden(css, MMM-Config_extension.css).

.m_compliments fieldset div[class$="-format"] {
  display:none;
}
and some will be shown when used.. (form_loaded event handler, MMM-Config_extension.js)<br>

oops.. NOW we have to fix the converter to handle putting/getting the JS object data to/from the form layout

it now looks like this

function converter(config_data, direction){
	const weather_list=[
                      "day_sunny",
                      "day_cloudy",
                      "cloudy",
                      "cloudy_windy",
                      "showers",
                      "rain",
                      "thunderstorm",
                      "snow",
                      "fog",
                      "night_clear",
                      "night_cloudy",
                      "night_showers",
                      "night_rain",
                      "night_thunderstorm",
                      "night_snow",
                      "night_alt_cloudy_windy"
                    ]
	if (direction == 'toForm'){ // convert FROM native object format to form schema array format
		// create entry array
		let nc = []
		// config format is an object, need an extendable array
		Object.keys(config_data.compliments).forEach(c =>{
			// for each key (morning, afternoon, eventing, date..., weather )
			// push an object onto the 'array '
			// the object must match the custom schema definition
			let x = c

			if(c.includes("^"))
				x = c.replace(new RegExp("\\^", "g"),'.')
			let when
			let df=null
			let field=null
			let entry = { when : x,  list: config_data.compliments[c]}
			// if the key contains space a space, then its the cron date/time type format
			if(x.includes(' ')){
				field='date-time-format'
				df=x
			} // if the key is weather related
 			else if(weather_list.includes(x)) {
				// weather
				field='weather-format'
				df=x
			}// if the object key contains a . or starts with a number, THEN its a date field
			else if(x.includes('.') || !isNaN(parseInt(x[0]))){
				field='date-format'
				df=x
			}
			// if we found a custom field, then fix the entry structure
			if(df){
				entry.when=field
				entry[field]=df
			}
			// save the new field structure in the array
			nc.push( entry)
		})
		// pass back a good copy of the data
		config_data.compliments= JSON.parse(JSON.stringify(nc))
		return config_data
	}
	else if (direction == 'toConfig'){  // convert TO native object from form array
		// create empty object
		let nc = {}
		// form format is an array , need an object for config.js
		config_data.compliments.forEach(e =>{
			// for each key (morning, afternoon, eventing, date... )
			// make an object entry from the two fields in each array element
			// as defined in the custom schema
			// special handling for the two date related values
			switch(e.when){
				case 'date-format':
				case 'date-time-format':
				case 'weather-format':
					// custom field, get the data from the right place in the structure
					nc[e[e.when]]=e.list
					break
				default:
					// default location for all others
					nc[e.when]= e.list
			}
		})
		// pass back a good copy of the data
		config_data.compliments= JSON.parse(JSON.stringify(nc))
		return config_data
	}
}
exports.converter=converter