Code Corner 7 Javascript - Nilpferdschaf/Egroupware-Doku GitHub Wiki

<< Prev.: Images and CSS

JavaScript

JavaScript has quickly become the status quo for creating rich interactive web apps and has been the most popular technology on StackOverflow developer surveys for over three years now. It is only natural that EGroupware integrates well with JavaScript. You will learn how it works in this section.

Setup

The first thing you need to do to start adding JavaScript is to create a new folder helloworld/js and then add a file app.js inside. Add the following code to this file.

app.js:
app.classes.helloworld = AppJS.extend({
    appname : 'helloworld',

    init : function() {
        this._super.apply(this, arguments);
    },

    et2_ready : function(et2, templ_name) {
        this._super.apply(this, arguments);
    },

    destroy : function() {
        this._super.apply(this, arguments);
    },
});

You are basically setting up a JavaScript library for your app that can then be used by EGroupware. Use the init() function to set up everything you need to use the library. destroy() is a destructor function that should contain everything necessary to clean up resources once the app is closed. Finally, et2_ready() is called when all eTemplate functionality is loaded and ready to go. The arguments to this function contain references to the eTemplate2 main object and the name of the template. All three functions should first call their parent functions.

Saving the et2 main object reference to a library variable is often useful, because it contains some helpful functions for manipulating the template. This can be done very easily by just including a variable et2 and initializing it to null. The variable will automatically be set to be a reference to the eTemplate object. Whenever you do this, you should not forget to clean up the reference in the destroy function.

app.js:
app.classes.helloworld = AppJS.extend({
    appname : 'helloworld',

    et2: null,

    init : function() {
        this._super.apply(this, arguments);
    },

    et2_ready : function(et2, templ_name) {
        this._super.apply(this, arguments);
    },

    destroy : function() {
        this._super.apply(this, arguments);

        delete this.et2;
    },
});

Executing Code on UI Widget State Change

Before you start adding more JavaScript, you will need to go to the eTemplate editor once more. Open the helloworld.index template and double click on one of the text boxes. The widget editor that opens has two options near the bottom that you have not yet looked at. Both onClick and onChange can be used to trigger JavaScript. Just select custom in the dropdown and write whatever JavaScript code you want in the text box next to it. This can be a simple "alert('hi');" or something like "app.helloworld.preview();", which will call a function preview() inside your app.js library.

Setting a custom onChange() function

Add this code to the text fields fname, sname and position and to the department select box dep_id. The preview() function will be triggered whenever the content of one of these ui elements changes and update a little preview text that shows the user what will be inserted into the database. To display the preview text, add a label above the submit button and set its Name to "preview-text".

The settings of the preview label

The following is an implementation of the preview() function. You can use it in app.js.

app.js:
preview : function() {
    var fname = this.et2.getWidgetById("fname").get_value();
    var sname = this.et2.getWidgetById("sname").get_value();
    var position = this.et2.getWidgetById("position").get_value();
    var dep_widget = this.et2.getWidgetById("dep_id")
    var dep_id = dep_widget.get_value();

    var department = 0;
    for (var i=0; i<dep_widget.options.select_options.length; i++) {
        if (dep_widget.options.select_options[i].value == dep_id) {
            department = dep_widget.options.select_options[i].label;
        }
    }

    var prev_text = this.et2.getWidgetById("preview-text");
    var prev_string = "";
    if (fname.length > 0) {
        prev_string += "If you click submit, you will be entered as " + fname;

        if(sname.length > 0) {
            prev_string += " " + sname;

            if (position.length > 0) {

                prev_string += ", working as " + position + " in " + department;
            }
        }
    }
    prev_text.set_value(prev_string + ".");
},

This code utilizes the reference to the et2 object and its getWidgetById() function. It works similar to document.getElementById() in that it returns the eTemplate 2 widget with the given id, which is set by the Name option in the eTemplate editor. Calling get_value() on an et2 widget returns its value, i.e. the content of a text field or the selected entry of a select box. The selected entry will be returned as an index number. To get the actual label of the entry, you need to access the options property of the select box. It contains, next to other information, the selection_options of the widget. Once you have those, you just need to get the correct label for the value.

Depending on what has already been input by the user, a string is generated that describes the information that will be written to the database. Lastly the content of the preview-text label will be set to this string.

Save the template and open your app. You can now start inputting a name and position. Whenever you switch to a new field, the text of the preview label will change to accomodate for your input.

No input set yet The preview label when only the first name is set The preview label when the first and second name a re set The preview label when everything is set.

The eTemplate 2 JavaScript Library

If you want to manipulate the user interface with JavaScript, it is helpful to know a few important functions that are part of the eTemplate 2 JavaScript library. These are all accessed through the et2 variable. You have already seen one of these functions, namely getWidgetById().

Class diagram of selected widgets and functions

As we have seen earlier, the getWidgetById() function can be used to get an eTemplate 2 UI widget object. Widgets all augment the et2_widget prototype. Its source code can be found at $egw_install/api/js/etemplate/et2_core_widget.js. The augmentations can be found inside the $egw_install/api/js/etemplate/et2_widget_$widgettype.js files.

If you want to know the architecture of the et2 JavaScript library, you can take a look at the following class diagram (click to enlarge). It does not contain every widget, attribute and function in the entire library, but it will give you a good overview about the different augmentations and functionalities.

A class diagramm of the et2 js library.

As you can see, there are a lot of possibilities to make your app behave exactly the way you want it to. To see how it works, you will now implement a simple password checker that informs users of how strong their password is. The end result is supposed to look like the screenshot below.

User interface with two password fields User interface with two password fields in action

To get started, add the two password boxes to the UI. This can be done in the eTemplate editor by chosing the widget Type Password. The first Password box has the Name "pass1" and the second one has "pass2".

Settings for the first password box

Now that the password boxes exist, it is time to make them interactive. Open your application's app.js file to add EventListeners to the password boxes inside the et2_ready() function.

app.js:
pass1: null,
pass2: null,

et2_ready : function(et2, templ_name) {
    this._super.apply(this, arguments);

    if (templ_name == "helloworld.index") {
        this.pass1 = this.et2.getWidgetById("pass1");
        this.pass2 = this.et2.getWidgetById("pass2");

        this.pass1.getInputNode().addEventListener("keyup", this.pass1_key_evt);
        this.pass2.getInputNode().addEventListener("keyup", this.pass2_key_evt);

        this.et2.getWidgetById("submit").set_readonly(true);
    }
},

The first thing that happens here is we create two additional references to pass1 and pass2 and initialize them using getWidgetById().

The next step is to call getInputNode() on them. This function can be called on any given et2_inputWidget and returns the DOM object that represents the widget in the browser. Adding the KeyListeners is now just a regular call to addEventListener().

One last thing you can do is disabling the submit button by calling set_readonly(). This way users can not submit the form for now. You will reenable it later once the password boxes are filled in correctly.

Now you need to implement the two EventListener functions pass1_key_evt() and pass2_key_evt().

app.js:
pass1_key_evt : function(evt) {
    var pass1 = app.helloworld.pass1;
    var pass2 = app.helloworld.pass2;
    pass2.set_value("");
    pass2.hideMessage();

    app.helloworld.et2.getWidgetById("submit").set_readonly(true);
    if (pass1.get_value().length < 8) {
        pass1.showMessage("Password needs to have 8 characters or more!","error");

    } else if (pass1.get_value().length < 12) {
        pass1.showMessage("Password is fine, but could be stronger", 'hint');

    } else {
        pass1.showMessage("Great password","success");

    }

    this.focus();
},

pass2_key_evt : function(evt) {
    var pass1 = app.helloworld.pass1;
    var pass2 = app.helloworld.pass2;

    app.helloworld.et2.getWidgetById("submit").set_readonly(true);

    var strong = pass1.get_value().length >= 8;
    var equal = pass1.get_value() == pass2.get_value();

    if (equal) {
        pass2.showMessage("Passwords match.","success");
    } else {
        pass2.showMessage("The passwords do not match!","error");
    }
    app.helloworld.et2.getWidgetById("submit").set_readonly(!(strong && equal));

    this.focus();
},

Two new functions are used in this code. showMessage() is part of baseWidget and displays a message to the user. It takes two arguments. The first contains the string that will be displayed to the user, the second one is an identifier string that specifies what type of message will be displayed. The three types of message are "validation_error", "hint" and "success". The example uses all three of them, so you can see what they look like. The other new function is hideMessage() which removes the message again.

The Firefox Developer Console and debugger

Once your projects get bigger, it can become quite painful to debug all of your JavaScript code. Most major browsers provide developer tools for this purpose, except using them with EGroupware can be a bit tricky unless you know a few tricks. The following hints can be used with the Firefox web browser.

The Developer Console

To open the console in Firefox, go to your open EGroupware tab and press ctrl+shift+k. To give you an idea of what you are looking at, go to the official developer console documentation. There is only one additional step you need to take before you can get going. EGroupware places your app inside an iFrame on the page. You need to select this iFrame as the currently targeted document, which can be done in the upper right corner of the developer tools.

How to select the right iFrame.

You can now directly access all JavaScript objects that are part of your app through the console. They are all referenced inside the app.helloworld global variable. Type this into the command line and press return. If you now click on the object that is being returned, you can see all its attributes on the right, including the pass1, pass2 and et2 variables.

The helloworld object.

Now it is time to play around. Try out the different functions from the library and see what they do and how they work. If you have no idea what a function might do, you are encouraged to check out its source code in the corresponding api/js/etemplate/et2_*.js file. The class diagram from earlier is very helpful for this, because it shows you how everything augments everything else.

Developer console:
>app.helloworld;
    Object { _super: undefined, egw: Object, et2: Object, pass1: Object, pass2: Object }
>var et2 = app.helloworld.et2;
    undefined
>var pass1 = app.helloworld.pass1;
    undefined
>var pass2 = app.helloworld.pass2
    undefined
>pass1.showMessage("This is a hint", "hint");
    undefined
>pass2.showMessage("This is a success", "success");
    undefined
>et2.getWidgetById("fname").set_value("Jim");
    undefined
>et2.getWidgetById("sname").set_blur("Clark");
    undefined
>et2.getWidgetById("position").set_height(50);
    undefined
>et2.getWidgetById("dep_id").set_multiple(true);
    undefined
>et2.getWidgetById("dep_id").set_height(150);
    undefined
>et2.getWidgetById("dep_id").set_width(150);
    undefined
>et2.getWidgetById("dep_id").set_value("0,2,3");
>var submit = et2.getWidgetById("submit");
    undefined
>submit.set_readonly(true);
    undefined
>submit.getDOMNode().style.background = "red";
    "red"
>submit.getDOMNode().style.transform="rotate(-30deg)"
    "rotate(-30deg)"

Playing around with the console.

The Debugger

You can open the debugger by pressing ctrl+shift+s. The official documentation for this tool can be found here. Like before, you need to select the right iframe in the top right corner, but once you have done that, you are good to go.

On the left, inside the debugging window, you will see a list of source files. All except app.js are provided by EGroupware. If you click on app.js, you should see the source code that you have written earlier. You can now start setting breakpoints and debugging as much as you want.

Conclusion

You should now have a good idea how the EGroupware JavaScript library works and how you can work with it in practice. This tutorial did not cover every function in detail, but you know how and where to find the missing information. The next and also last section will cover some other tools and functions that might help you in the future.

Download

You can download the result of this section here.

Next: Other Helpful Tools >>

^ Back to top