Dynamic values into forms

I want to reuse the value of a previous field into a form to initialize a new field or better a new section of the form, depending of the reference value

How to proceed ?

Jean-Marie

@jms27000 I think that you are asking how to use the value of a field on a form to change the value / options available for another field on that same form? If so: the only ability we offer to do that sort of thing is through writing JavaScript and adding it to form.js. Other than the file picker, the most advanced custom logic we have written thus far is for OSC’s Ansys Workbench where we conditionally show/hide a field based on the user’s selections, and change the available number of CPU cores based on which compute node type the user wants.

Hi. Morgan

Thanks for your answer. It’s the idea. I will test it when I’ll be back from vacations.

Jean-Marie

Hi, Morgan,

back from vacations but not still at work. as you know, we will use the file_picker function in each form.yml.erb we’ll have to generate. a good idea seems to let the “file_picker” file as you send it and to avoid to modify it.

question : how to create a generic form.js file which realizes the include of all other “js” files located in this app directory ?

may be you prefer that i open a new topic on that subject ?
thanks a lot.

jean-marie

We can answer this question here; it’s fine. In general I think your preference to avoid changing 3rd party code is probably a good idea unless you need to fix something (and even then pull requests on the main repo are welcome!). I suggest the following three options:

  1. Add a file named $APP_ROOT/local/.js and put your code there; this takes advantage of our support for the notion of sub-apps (which I do not understand very well). Given that you would be adding code to a ‘hidden’ file this is by far my least favorite solution.
  2. Fork the bc_js_filepicker project, modify src/main.js to include code specific to your project, build it yourself (using NodeJS 10+), and then just include form.js as usual.
  3. Add another custom initializer to /etc/ood/config/apps/dashboard/initializers that alters BatchConnect::App#custom_javascript_files to return additional app-specific JavaScript files.

An example of the last:

require 'batch_connect/app'

module BatchConnect
  class App
    # Return any custom JS files that exist at root in order
    # 
    # Use numeric prefixes to control load order: 00_file.js, 01_file.js, ...
    # 
    # @return [Pathname] paths to custom javascript files that exist
    def custom_javascript_files
      (
        Pathname.glob(root.join('*js')) +
        # Add the JS file for a possible sub_app
        [sub_app_root.join("#{sub_app}.js")]
      ).select(&:file?).sort
    end
  end
end

Hi, Morgan

i have spent some time to test the dynamic form with the following context :

  • rm_cache.rb : activated
  • several_js.rb : activated
  • form.yml.erb
  • usage of javascript : file_picker.js & dyn_form.js

my test :

  • copy the “working_dir” (data-picker type) to “inputfile” (data-picker type) automatically
  • hide a “test1” field (text_field type) if “node_type” (select type) value is 16. if not 16, show test1 and hide test2 (text_field type)

the result is OK, but not realized like you done with the Workbench example, because i have introduced a kind of loop

is it the good way to follow ? thanks for your reading

jean-marie

form.yml.erb :

<%-


   name = "Fluent"
   name_down = name.downcase
   name_up = name.upcase
   vnc_disp = qdisplay
   projects = qprojects
   hold_jids = qhold_jid (name)
   versions = qversion(name_down)
   my_work_dir = qdatadir
   max_cores = qnb_cores
   free_cores = qnb_free_cores


-%>---
cluster: "verhpc"
form:
  - working_dir
  - type_node
  - test1
  - test2
  - inputfile


attributes:
  working_dir:
    label: "Working Directory"
    data-filepicker: true
    readonly: false
    help: "Type the workingdir you want to work in :"
    value: "<%= my_work_dir %>"

  test1:
    widget: "text_field"
    label: "test1"
    value: ""
    help: "Description of the test1"
  test2:
    widget: "text_field"
    label: "test2"
    help: "Description of the test2"

  inputfile:
    data-filepicker: true
    readonly: false
    value: "<%= my_work_dir %>"

  type_node:
    widget: select
    label: "Type_Node"
    help: "You can't leave this blank"
    options:
      - [ "DELL_16",   16   ]
      - [ "DELL_28",   28   ]
      - [ "DELL_32",   32   ]
    value: "DELL_16"
    required: true

dyn_form.js :

'use strict'

/**
 * Toggle the visibilty of a form group
 *
 * @param      {string}    form_id  The form identifier
 * @param      {boolean}   show     Whether to show or hide
 */
function toggle_visibilty_of_form_group(form_id, show) {
    let form_element = $(form_id);
    let parent = form_element.parent();

  if(show) {
    parent.show();
  } else {
    form_element.val('');
    parent.hide();
  }
}

/**
 * Toggle the visibilty of the test fields
 *
 */
function toggle_tests_field_visibility() {
    let type_node = $("#batch_connect_session_context_type_node");
    toggle_visibilty_of_form_group(
    '#batch_connect_session_context_test1',
    type_node.val() === '16'
    );
    toggle_visibilty_of_form_group(
    '#batch_connect_session_context_test2',
    type_node.val() !== '16'
    );
}

/**
 * Sets the change handler for the node_type select.
 */
function set_node_type_change_handler() {
    let node_type_input = $('#batch_connect_session_context_node_type');
    node_type_input.change(toggle_tests_field_visibility);
}

function set_node_inputfile_change_handler() {
  let inputfile_type = $('#batch_connect_session_context_inputfile');
  let working_dir_type = $('#batch_connect_session_context_working_dir');
  let the_dir = working_dir_type.val();
  if (the_dir !== old_dir) {
	inputfile_type.attr('value', the_dir);
	old_dir = the_dir;
  }
}

/**
 * Install event handlers
*/
function main() {
	$(document).ready(function() {
	  // Ensure that fields are shown or hidden based on what was set in the last session
	  toggle_tests_field_visibility();

	  set_node_type_change_handler();
	  set_node_inputfile_change_handler();
	});
};

/* main */
let old_dir = "none";
setInterval(main, 5000);

A few comments on this:

  • setInterval should be setTimeout if you only intend for the function to execute once: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
  • on the other hand if the purpose of the timeout is to continually update one form element based on the content of another a better solution is to use an event handler like .keyup so that there is no lag between the user finishing typing and the update occurring.
  • If the purpose of the timeout is to wait for the File Picker buttons to appear, it may not be necessary because you are getting values from the input tags which are not generated by the File Picker
  • It is unnecessary to use $(document.ready(function() {...}) in Batch Connect form.js files because the contents of those scripts are concatenated into the document itself rather than linked via script tags in the document header; some day I will need to update the Abaqus app and I will update its form to remove the ready event listener
  • The function set_node_inputfile_change_handler is not actually setting a change handler for anything

Hi, Morgan and thanks for these informations. As I’m not specialized as a developper, all your infos are’nt clear in first reading, but with time, it’ll be ok for me. What do i have to do to have a
" set_node_inputfile_change_handler" function operational ?

Have a good weekend.

jean-marie

The set_*_change_handler functions are meant to install HTML event listeners to avoid having to write our own event loops like you did with setInterval. The event listeners which I called handlers, are what actually do the work when the event occurs.

So for the node_inputfile element I would do something like this:

function set_node_inputfile_change_handler() {
    let inputfile_type = $('#batch_connect_session_context_inputfile');
    // Handle typing
    // Note that the file picker does not generate events at this time.
    // I am going to release an updated version that will emit a keyup event.
    inputfile_type.keyup(handle_inputfile_changes);
}

function handle_inputfile_changes(event) {
    // Wrap the input with jQuery for convenience
    let inputfile_type = $(event.target);
    // Do something with your input
}

I just updated the file picker to version 0.3.0 which emits a keyup event whenever a file is picked. That will let you use event listeners that will work even when the file picker is what changes the input element. I also added the ability to set the Favorites menu through another data attribute in form.yml.

  file_picker_input:
    data-filepicker: true
    data-file_picker_favorites: '[{"title": "Team Project", "href": "/fs/project/PZS0714"}]'

Where data-file_picker_favorites value is expected to be a string containing JSON in the format:

[
    {
        "title": "...",
        "href": "..."
    },
    ...
]

Hi, everybody,

Morgan, i have tested your file-picker v0.3.0 with the keyup event as proposed.

  • the file-picker works fine and the fact to populate the “data-file_picker_favorites” field with a variable’s content by example avoids to see the / of the system. :smile:
  • there’s no more need of the “setInterval” function when you’re using the keyup event :smile:

good job ! thanks a lot.

jean-marie