Wanted to create a place for users to share some of the CSS or Java that they find themselves pasting in every. single. form. they author as discussed in Winding Down With a Workflow at Empower 22.
Discussion
Discussion
Share Standard CSS or Java You Use on Every Single Form You Create
Not ALL forms, but in Classic Designer I've had to disable or hide and replace the submit button so field rules can be used to show or hide the button. Modern Designer has that built into field rules, so not needed there.
// Goes into HTML Field to create a new submit button. <button type="submit" class="action-btn" style="background-color:#00543c; border-color:black; color:white">Submit</button> // Hide the Submit Button $('.Submit').hide(); //Show the Submit button again $('.Submit').show(); //Disable the submit button $('.Submit').attr('disabled', 'disabled'); //Enable the submit button $('.Submit').removeAttr('disabled');
When I need to save to the repository, or for when I have selected "Show submitted form" on event completion and need to be able to pull field data to run the code; I check to see if the form is in display/read-only mode. Then I use this when I need to pull field values. lines 12-20 are just to illustrate how the variable isReadOnlyView is used. This is also handy when making use of the monitoring tab and viewing past steps in a process.
Because Laserfiche does not save the value when you set read-only on the layout tab, I have a standard class that I put at the top of my JavaScript sets fields manually to read only when needed.
$(document).ready (function () { /* set if the form in is read only mode or not */ var isReadOnlyView = (!$('.aField input').length == 1); /* as needed set fields as read only at this point instead of on the form so */ /* they keep the updates manually set in the code below. */ $('.readOnlyField input').attr('readonly', true); $('.readOnlyField textarea').attr('readonly', true); $('.readOnlyField .ui-datepicker-trigger').hide(); var somVar; if (isReadOnlyView) { someVar = $('.aField div.ro').text(); } else { someVar = $('.aField input').val(); }; }); /* END: $(document).ready (function () */
I wrote some code for replacing the default "collapsible" icons in the classic designer with whatever you want (I used FontAwesome chevron icons):
Custom HTML on the form:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
CSS:
.collapsible h2, .collapsible.collapsed h2 { background: inherit; } .collapsible h2 i { float: right; }
JS:
$('#q7>.collapsible').html('<h2>Section 1<i class="fa-solid fa-chevron-up"></i></h2>'); $('#q8>.collapsible').html('<h2>Section 2<i class="fa-solid fa-chevron-up"></i></h2>'); $('#q9>.collapsible').html('<h2>Section 3 Instruction<i class="fa-solid fa-chevron-up"></i></h2>'); $('.collapsible').on('click', function() { if ($(this).children().children().hasClass("fa-chevron-up")) { $(this).children().children().removeClass("fa-chevron-up"); $(this).children().children().addClass("fa-chevron-down"); } else { $(this).children().children().removeClass("fa-chevron-down"); $(this).children().children().addClass("fa-chevron-up"); } });
The custom HTML field will link to the FontAwesome CSS file. The CSS will hide the original icon and orient the <i> tag to the correct spot. The JS will replace the HTML for the section header to include the icon you want to use, and then will swap on each click to the opposite direction. This code assumes each section is default expanded. If you default to collapsed you'll have to swap the icon directions initially.
Working with Numbers and Math
Since the built in function, Number(), does not parse forms number field data due to the commas, I use this function all the time as an alternative
function Num(strNumber) { strNumber += ''; strNumber = strNumber.split(',').join(''); return Number(strNumber); }
Reading Table Values as an Array
Working with values from a table is common and working inside of a loop can be confusing. This function returns a single object that represents the entire table.
function GetTableValues() { //Create the object that will return with the table's values var columns = {}; //For each column, create an array inside the columns object var tableName = arguments[0]; for (var i = 1; i < arguments.length; i++) { columns[arguments[i]] = []; } //For each column array //You could loop through rows, then columns but not sure if either is more performant Object.keys(columns).forEach(function (key) { //For each row in the table $('li.' + tableName + ' tbody tr').each(function () { //Get the value and add to the array var rowValue = $(this).find('.' + key + ' input').val(); if (rowValue) { columns[key].push(rowValue); } else { //We could not get a value from an input, try select rowValue = $(this).find('.' + key + ' select').val(); if (rowValue) { columns[key].push(rowValue); } else { //We could not get a value from an input or a select, likely just an empty value //Push empty into the array columns[key].push(""); } } }); }); return columns; }
Usage:
var myTable = GetTableValues('tableClassName', 'columnClassName1', 'columnClassname2', etc); var column1row3value = myTable.columnClassName1[2];
Create objects on the fly
Sometimes I want to create a message, button, etc on the fly. This method allows me to do so and set the parent object by class to determine where it shows up on the form.
function Instantiate(newClassName, parent = 'cf-formwrap', type = 'div') { var newObject = document.createElement(type); newObject.classList.add(newClassName); if (typeof parent === 'string') { document.getElementsByClassName(parent)[0].appendChild(newObject); } else { parent.appendChild(newObject); } return newObject; }
Usage, causing "Hello World" to appear under an input with class name someInput
var HelloWorldMessage = Instantiate('helloMessage', 'someInput'); HelloWorldMessage.html('Hello World');
Destroy Instantiated Objects
function Destroy(element) { element.parentNode.removeChild(element); }
Converting Input Fields to Hyperlinks
Sometimes workflow is setting the value of a variable with a link to a file in the repository, or a user is entering something like a Google Maps or Amazon link into a form. This turns an input field into a working hyperlink that other users can click on.
function ShowLink(event) { if (event.target.value == '') return; if (event.target.parentNode.childNodes.length > 1) { event.target.parentNode.childNodes[1].innerHTML = '<a href="' + event.target.value + '" target="_blank">' + event.target.value + '</a>'; } else { var newObject = document.createElement('div'); event.target.parentNode.appendChild(newObject); newObject.innerHTML = '<a href="' + event.target.value + '" target="_blank">' + event.target.value + '</a>'; } }
Usage:
$('.link input').on('change',ShowLink); $('.link input').mouseover(ShowLink);
I'm still on verion 10.
I have a template that I use for every new form that has a notes table at the bottom. Users can add notes to the fields, like if they are doing an approval step and want to ask the initiator a question and return to them, or notes to explain what they are doing. It has Javascript that automatically posts a note when they Submit/Reject/Approve the task. I like this format of notes better than the built-in Comments.
The form also includes a bunch of CSS classes for laying out fields.
CSS:
/*hide the delete button on the Notes table*/ .notesTable .form-del-field {display:none!important;} /*eliminate the default setting of green highlighting when a field is populated correctly*/ input.parsley-success, select.parsley-success, textarea.parsley-success { color : #555; background-color : white; border-width : 1px; border-color : #A9A9A9; } /*hide the fields that are always hidden*/ .approval-wrap p, #comments {display: none;} .hiddenField {display: none!important;} .loadingHiddenField {display: none!important;} /*set re-usable field layouts*/ .noLabel .cf-label {display : none;} .fifteenPercentWidth {display : inline-block; width : 15%;} .fifteenPercentWidth .cf-field {min-width : 80px;} .quarterWidth {display : inline-block; width : 25%;} .thirdWidth {display : inline-block; width : 33%;} .halfWidth {display : inline-block; width : 50%;} .fortyEigthPercentWidth {display : inline-block; width : 48%;} .twentyFourPercentWidth {display : inline-block; width : 24%;} .threeQuarterWidth {display : inline-block; width : 75%;} .dateField {display : inline-block; width : 25%;} .dateField .cf-xlarge {width : 80%;} .twentFourPercentDateField {display : inline-block; width : 24%;} .twentFourPercentDateField .cf-xlarge {width : 80%;} .wideLabel .cf-label {width : 300%; max-width : 300%;} .alignTop {vertical-align: text-top;}
Javascript:
$(document).ready(function () { //add a note regarding form routing when the Reject button is clicked. $(".action-btn.Reject").on('click', function() { var noErrors = true; //remove required fields - allowing user to reject form (cancel) without //having completed all of the required fields. //***REMOVE COMMENT CHARACTERS ON NEXT 4 LINES (AND DELETE THIS LINE) IN ORDER TO ENABLE THIS FUNCTIONALITY TO BYPSS REQUIRED FIELDS ON FORM REJECT //$('*[required]').each(function() { //$(this).removeClass('required') //$(this).removeAttr('required'); //}); $('#form1').parsley().validate(); $('.parsley-error').each(function() { noErrors = false; }); if (noErrors) { //update the notes with the current action if (($('.notesTable tr:last').find('.notesUser input').val() == $('.getCurrentUserFullName').text()) && ($('.notesTable tr:last').find('.notesNotes textarea').val() == "")) { $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Reject').val()); } else { $('.notesTable .cf-table-add-row').trigger('click'); $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Reject').val()); } //end of if...else... block } //end of if (noErrors) { }); //end of $(".action-btn.Reject").on('click', function() { //add a note regarding form routing when the Submit button is clicked. $(".action-btn.Submit").on('click', function() { var noErrors = true; $('#form1').parsley().validate(); $('.parsley-error').each(function() { noErrors = false; }); if (noErrors) { //update the notes with the current action if (($('.notesTable tr:last').find('.notesUser input').val() == $('.getCurrentUserFullName').text()) && ($('.notesTable tr:last').find('.notesNotes textarea').val() == "")) { $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Submit').val()); } else { $('.notesTable .cf-table-add-row').trigger('click'); $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Submit').val()); } //end of if...else... block } //end of if (noErrors) { }); //end of $(".action-btn.Submit").on('click', function() { //add a note regarding form routing when the Approve button is clicked. $(".action-btn.Approve").on('click', function() { var noErrors = true; $('#form1').parsley().validate(); $('.parsley-error').each(function() { noErrors = false; }); if (noErrors) { //update the notes with the current action if (($('.notesTable tr:last').find('.notesUser input').val() == $('.getCurrentUserFullName').text()) && ($('.notesTable tr:last').find('.notesNotes textarea').val() == "")) { $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Approve').val()); } else { $('.notesTable .cf-table-add-row').trigger('click'); $('.notesTable tr:last').find('.notesNotes textarea').val("Task: " + $('.getCurrentStepName').text() + "\nFunction: " + $('.Approve').val()); } //end of if...else... block } //end of if (noErrors) { }); //end of $(".action-btn.Approve").on('click', function() { }); //end of $(document).ready(function () {
Change tab title.
$('title').text("Your Title Here");
I do the inline like this as a percent.
.inline50 {display:inline-block;width:50%!important;} .inline30 {display:inline-block;width:30%!important;} .inline33 {display:inline-block;width:33%!important;} .inline25 {display:inline-block;width:25%!important;} .inline20 {display:inline-block;width:20%!important;}
This CSS turns off the asterisk without removing the required class. I find this looks nicer and apply it to most of my forms.
.form-q .parsley-errors-list.filled { display:none; } .form-q .cf-required {display: none;}
----- CSS Settings - put text above input boxes and center format title ----- #form-title-wrap {text-align: center;} .cf-field, cf-label {width: 100% !important; text-align:left;} .choice {width: 25%;} div.btn-wrapper {display:block} .Submit {display:block;margin:auto} /*Displays two fields per line*/ .TwoPerLine{display: inline-block; width:47%;float: left;} .TwoPerLine .cf-field input {width: 75%;} .TwoPerLine .cf-medium {width:97%;} /* Displays three fields per line */ .ThreePerLine {display: inline-block; width: 31%; float: left;} .ThreePerLine .cf-medium {width:97%;} /* Displays four fields per line */ .FourPerLine {display: inline-block; width: 24%;} .FourPerLine .cf-medium {width:97%;} /* Displays five fields per line */ .FivePerLine {display: inline-block; width: 19%;} .FivePerLine .cf-medium {width:97%;} /* Centers the section headers */ .SectionCenter .cf-section-header, cf-label {text-align: center;} /* Center custom html fields */ .HTMLField {text-align: left;font-weight: bold;color: black;} .HTMLCenter {text-align: center;font-weight: bold;color: black;} /* Checkbox labels on the left not above */ .ChkBox .cf-label {width: 65% !important; text-align:left;} .ChkBox .cf-field {width: 35% !important; text-align:left;} .ChkBox .cf-field, .cf-label {display: inline-block;} /* .ChkBox .cf-label { border-style: solid; border-color: red;} */ /* .ChkBox .cf-field { border-style: solid; border-color: green;} */ /* .ChkBox .choice { border-style: solid; border-color: blue; width: 25% !important;} */ /* This makes the background for read-only fields white instead of gray */ .cf-formwrap input[readonly]{ background-color:#ffffff!important; border:0px; } .cf-formwrap select[readonly]{ background-color:#ffffff!important; border:0px; } .cf-formwrap textarea[readonly]{ background-color:#ffffff!important; border:0px; }
This is what I have.
Also, you can use bootstrap css codes in the css fields: https://getbootstrap.com/docs/3.3/css/#grid
For Classic Designer users (v10 or v11) we use this on every form to hide the default Comments field (we create our own for each reviewer):
.approval-wrap p, #comments {display: none;}
Hope this helps someone else! :)