Using Javascript in ELMA
To implement various data display that would be convenient for users, you often need more than the basic capabilities of ELMA Designer.
To provide dynamically interactive web pages, JavaScript (JS) is used, which is executed by the browser on the user's computer.
JS works with web pages via the DOM (document object model) interface. According to this interface, a page is represented as a tree of objects, which all have properties. Through these properties, the page content can be managed and various interactive scripts can be executed.
Let's take a look at some examples of using JS to implement different capabilities in ELMA. The developer's tools of Google Chrome will be used.
Open the required document management task tab and hide excessive tabs
Basic document management tasks ("Document acquaintance", "Document approval") do not allow creating task forms in the Form Builder. Therefore by default, it contains the following tabs: Acquaintance (contains the information on the document, added to the task), Preview, About Task, History, Document.
Customers often require the simplest interfaces and the fewest actions for users. When opening an acquaintance task, it makes sense to go to the Preview tab immediately and hide "excessive" (for an ordinary user) tabs.
Open the task and the developer's tools (F12). First, implement switching to the "Preview" tab when opening the page. Main objectives:
- Find the required elements in the page object model.
- "Imitate" clicking on the element.
For guaranteed element search you must define some key element parameters. Click on the element selection button on the page and select the Preview tab header. In the element tree of the Elements tab in the developer's panel, you will see the HTML of this element (fig. 1).
Fig. 1. Selecting an element on the page and its display in the page object tree
This element has attributes: class, href. A unique enough element ID is a part of the value of the href - PreviewTab attribute. To make sure that the page does not contain other elements, whose href attribute contains a similar sub-string, you can search this text in the page's source code (Ctrl+U).
With this "unique" string, you can find the element in the page's DOM using jQuery - a set of special functions for simpler interaction between JavaScript and the web page. Elements are searched with selectors. In this example, a selector for searching an element by an attribute value ([attribute$=value]) is used. Find the required element by the part of the href attribute value. For this, you need to use non-strict search. The resulting structure should look like this:
$('a[href*="PreviewTab"]')
The * character indicates that elements are searched by a part of a string. You can find other use cases of functions and parameters on the Internet. The result of this function's execution is a collection. You can test a function on the Console tab of the developer's tools in Google Chrome (fig. 2).
Fig. 2. JS function execution debugging in Chrome
After executing the function, its execution result will be displayed, in this case, the found collection of elements. Since you need the element itself, you need to address it through the index. We also recommend that you implement a check that the collection contains elements (otherwise, the rest JS functions on the page may not be executed in case of an error). To open the tab, the click() page element method is used, which imitates a mouse click on the required element.
The resulting Javascript for searching an element and clicking will look like this:
var prevTabList = $('a[href*="PreviewTab"]'); if (prevTabList.length>0) { prevTabList[0].click(); }
To hide excessive tabs, you also need to find the required elements of the page DOM and change their display style. Let's hide the About Task, History, and Document tabs. There is also an "open in a new tab" link next to the Document tab header, let's hide it as well. You can use the tools, described above (page element selection).
Figure 3 shows information about the elements, related to the task tabs. Find a key attribute in each element, which can be used to define an element.
Fig. 3. Page DOM elements, related to tabs
Also, implement the required checks for each selector. Example of JS code for hiding all the tabs:
var listTaskInfo = $('a[href = "#2-3"]'); if (listTaskInfo.length>0) { listTaskInfo[0].style.display = 'none'; } var listHistory = $('a[href*="Common/EntityHistory/"]'); if (listHistory.length>0) { listHistory[0].style.display = 'none'; } var listDoc = $('a[href*="/Documents/Document/TabView"]'); if (listDoc.length>0) { listDoc[0].style.display = 'none'; } var listOpenDoc = $('a[href*="/Documents/Document/View"]'); if (listOpenDoc.length>0) { listOpenDoc[0].style.display = 'none'; }
To execute this code (switching to the required tab and hiding the others) automatically when loading the page of an acquaintance task, you need to embed it in the code of the page. To do so, open the settings of the "Document Acquaintance" task in ELMA Designer. On the Context tab, add any variable and open its properties. On the System tab, click Edit in the Razor Form field (fig. 4).
Fig. 4. Binding a Razor form to the main task form
In the opened window with Razor markup, click Edit. Copy the entire code from the default view (so that this element was displayed as designed in the system). At the end of the code, add your code:
<script type="text/javascript"> //to execute the code then opening the page, it must be bound to the page loaind event (load) $(window).load(function() { //find and "click" on the Preview tab var prevTabList = $('a[href*="PreviewTab"]'); if (prevTabList.length>0) { prevTabList[0].click(); } //find and hide the About Task tab var listTaskInfo = $('a[href = "#2-3"]'); if (listTaskInfo.length>0) { listTaskInfo[0].style.display = 'none'; } //find and hide History tab var listHistory = $('a[href*="Common/EntityHistory/"]'); if (listHistory.length>0) { listHistory[0].style.display = 'none'; } //find and hide the Document tab var listDoc = $('a[href*="/Documents/Document/TabView"]'); if (listDoc.length>0) { listDoc[0].style.display = 'none'; } //find and hide the Open in new tab link var listOpenDoc = $('a[href*="/Documents/Document/View"]'); if (listOpenDoc.length>0) { listOpenDoc[0].style.display = 'none'; } //move to the top of the page window.scrollTo(0, 0); } ); </script>
Figure 5 shows the code structure in the Razor form of the element.
Fig. 5. Editing Razor
After saving and publishing the process, the acquaintance task form will look in the browser as shown in figure 6.
Fig. 6. JS script execution result
Saving and processing a process context when clicking on a button on the task form
Consider an example, when you need to change a process context when clicking on a button on the task form and save it. At the same time, the task should not complete (the button should not be a transition from the process task).
If you use a regular script, which changes the value of a context variable, the context will change on the current form, but it will be saved in the instance only after making a transition in the process or clicking Actions - Save.
Thus, the solution, in this case, consists of three steps:
- Change the process context on the form.
- Track the context change event.
- "Imitate" clicking on the Actions - Save button.
As an example, let's change the value of a User type variable from empty to "Administrator" by clicking on the button next to the context variable value.
Add a "Changed User" variable to the process context, "ChangedUser" property name. Also, add a "NeedSaveContext" string variable, its purpose will be explained later. Use the task form builder and place the elements onto the form (fig. 7).
Fig. 7. Example of a task form with the change context button
Add a script to the button, which will change the value of the main context variable and the "NeedSaveContext" auxiliary variable.
public virtual void ChangeUser (Context context, EleWise.ELMA.Model.Views.FormViewBuilder<Context> form) { //change the auxiliary variable context.NeedSaveContext = "yes"; //change the main context variable: you can change any number of context variables and call other scripts context.ChangedUser = EntityManager<User>.Instance.LoadOrNull(1L); }
The field with the auxiliary variable must be editable! For the convenience of users, you should hide it. You can do it in a script, executed when loading the form.
public virtual void OnFormLoad (Context context, EleWise.ELMA.Model.Views.FormViewBuilder<Context> form) { form.For(c => c.NeedSaveContext).Visible(false); }
To imitate clicking Actions - Save, you need to find out what actions are performed when clicking it. Open the process task form in the browser, open the developer's tools and select the required element in the toolbar menu. You are looking for the "onclick" handler (fig. 8).
Fig. 8. Inspecting the Actions - Save button
The following JS is executed with the "onclick" event:
$('#SelectedConnectorUid').val('00000000-0000-0000-0000-000000000000');submit_ignore_validate($('form:first'));
You need this code to be executed when clicking on the Change User button.
Next, add a panel to the form. In its settings, select No style and empty header. Edit the Razor markup of the panel as described above (fig. 4).
Add the following code to the markup:
<script type="text/javascript"> //ajaxSuccess - is an event of getting any information from the server. It is executed after loading a form, when triggering any dynamics on the form, etc. $(document).ajaxSuccess(function(event, xhr, setting) { //All the fields on the form are matched with the process context and have ID = "Entity_<ContextVariableName>. //However, you can address attribute values only of those fields, which are available for editing (even if they are hidden on the form with a script) //Compare the attribute value with the one, that was set with the script on the button if ($('#Entity_NeedSaveContext ').val()== 'yes') { //before reloading the page, reset the flag indicating that an empty string must be saved to the context. It will allow not executing this JS when loading the page after saving the context (since the ajaxSuccess event will also be executed in it) $('#Entity_NeedSaveContext ').val(''); //imitate clicking on the Actions - Save button $('#SelectedConnectorUid').val('00000000-0000-0000-0000-000000000000'); submit_ignore_validate($('form:first')); } } ); </script>
Save and publish the process. When clicking Change User the process context variable "Changed User" will be filled in. The context will be saved immediately after (fig. 9). The page will be reloaded, therefore if other context variables are changed manually, they will also be saved.
Fig. 9. Result of clicking the button
It may occur so that the button is not on the main task tab or in the middle of the page. In this case, for the convenience of users, you should scroll the page to the same spot, where the user clicked the button.
Simply adding the appropriate page positioning functions and tab click imitation to the onLoad event would not work, because they will be executed every time the task page is opened, not only after clicking on the button.
For such a solution, you can set a cookie with limited time.
The Document object has a cookie string attribute, which can be read by calling the document.cookie function on the Console tab of the developer's tools.
To set cookie to the body of the function described above (executed upon the ajaxSuccess event), add it at the beginning.
//calculate the cookie lifespan, in this case, 2 seconds from the current moment. The time depends on the page loading speed var date = new Date(new Date().getTime() + 2 * 1000); //set cookie with the value "ButtonWasPress" and the time defined above document.cookie = "ButtonWasPress; path=/; expires=" + date.toUTCString();
Thus, after reloading the page, you can check that there is a cookie, by implementing the function, bound to the page loading event.
$(window).load(function() { //check that there is the cookie if (document.cookie.indexOf("ButtonWasPress")!=-1) { //"scroll" the browser page 100 px down window.scrollTo(0, 100); } } );
Instead of scrolling the page you switch to another tab.
Note, that in this example, the cookie is saved for all the pages of this site, and is available for two seconds on any page of the ELMA server. If other forms use similar checks and the same cookie name, it will also trigger the javascript. It can be remedied by checking the page header or setting different cookie names on different task forms.
These examples allow tackling numerous small interface improvements, which are impossible to implement with C# scripts in ELMA Designer.
JS allows creating complex interaction between a browser and the ELMA server, requesting, obtaining data and dynamically creating pages.