Migration guide

Breaking changes in the New UI and Why we have done these changes

When services uses JavaScript to directly manipulate the DOM of a page there is no way for us to guarantee that services will continue to work after an upgrade. Changes could have been made by the Zervicepoint Dev teams that affect the rendering of a page in such a way that your direct DOM manipulations no longer work or create undesirable side effects. Therefore, we have decided to remove the following functions from the ZAPI.

  • getElement()
  • get$Element()
  • getContainer()
  • get$Container()

These were helper functions that gave access to the underlying DOM elements of Fields.  But now we want to steer the behavior towards always referencing form fields through the abstraction layer that the ZAPI provide. That is, not referencing DOM elements directly, either by using the previously supported functions listed above or in any other way including JQuery selectors.

We are aware that there are quite a few services out there that make use of DOM manipulations and we want to make everything we can to make your transition to the new UI as smooth as possible. Therefore we have created this document where we are listing the most common direct DOM manipulations we have seen at our customers and supply better alternatives that are supported over time.

Detecting services that will experience issues in new UI

Before starting using the new UI you can locate services that have forms containing JavaScript using not supported DOM-Manipulations by running a SQL query against your ZervicePoint database. See How to find processes with DOM-manipulations and export to excel.

The query will return all services that make use of the deprecated ZAPI functions listed above as well as JQuery and other ways of manipulating the DOM.

What do I do now?

The next step is that for each service identified, deduce why direct DOM manipulations are used and find possible alternative solutions with help from the list below. If you run into trouble getting your service to run in the new UI and can't get help from the list below, then we are happy to help you out. Contact us through our service desk which you can reach here: http://zervicepoint.com/helpandsupport/.

Common direct DOM manipulation/access scenarios

Custom loading indicator

Don't do this...

Z("#Status").first().get$Container().append("<div id='loadStatus'></div>")

$('#loadStatus').html('<img src="/Content/images/select2/spinner.gif" align="absmiddle"> Loading ...')

Instead do this...

Use the supported .spin() function in the ZAPI (Zervicepoint Javascript API).

Z("#Status").spin()

Custom validation

Don't do this...

Z("#Status").first().get$Element().css("border", "1px solid red")
Z("#Status").first().get$Container().append("<div id='message'>Error!</div>")

Instead do this...

Use the .valid and require functions in the ZAPI (Zervicepoint Javascript API) to build your validation logic.

Z("#InputText").valid(false, "Error!", true)

Z("#InputText").require(true)

Changing field size

Don't do this...

Z("#Status").first().get$Container().css("width", "600px")

Instead do this...

Use field size setting on field in the form designer.

Dynamic text messages (injecting DOM elements)

Don't do this...

Z("#Status").first().get$Container().append("<div id='message' style='display: none'>Some message only shown under certain conditions...</div>")
$('#message').show();

Instead do this...

Add RTF blocks or read only text fields to the form and control content and visibility through use of the ZAPI (Zervicepoint Javascript API).

Z("#TextBlockRTF").show()
Z("#TextBlockRTF").hide()

Line feed, put form field on new row

Don't do this...

Z("#InputText").first().get$Container().before("<div style='width:100%'><div>")

Instead do this...

Utilize form section and control alignment of fields by row or column. Or, use add an empty Text block to the form in order to force a line break.

Dynamically set fields as readonly and disable/enable

Don't do this...

Z('#Status').first().get$Element().attr("readonly", true)

Instead do this...

Use .enable/.disable in ZAPI (Zervicepoint Javascript API).

Z('#Status').disable()
Z('#Status').enable()

Hide/Show form fields

Don't do this...

Z('#Status').first().get$Container().css('display', 'none')

Instead do this...

Use .hide/.show in ZAPI (Zervicepoint Javascript API).

Z('#Status').show()
Z('#Status').hide()

Scrape form for use in email

Use new GetOrderDetails script making use of the ZAPI (See Construct an order details summary client side).

Disable/Enable submit button

Don't do this...

$("#submit-button-container").hide()

Instead do this...

Use ZAPI and disable the form (Zervicepoint Javascript API).

Z('form:').disable() 

Window load

Don't do this...

$(window).load(function() {
});

Instead do this...

var field = Z('#Identity');
function test() {
    if (field.val() !== '' && field.val() !== undefined && field.val() !== null && field.val().length > 0) {
        //Put your actions here..
        clearTimeout(minFunc);
    }
}
var minFunc = setInterval(test, 100);

OnChange

Don't do this...

$(Z('#User').first().get$Element()).bind('change', function() {
});

Instead do this...

Z('#User').change(function() {
});

OrderDetailsHTML

Don't do this...

Z.getOrderDetailsHtml = function() {
    var reshtml = '';
    var f = Z.getForms()[0];
    if (f) {
        var fsections = [];
        $(f.items).each(function () {
            if (this.type == 'fieldSet') {
                fsections.push({
                    'elementid': this.elementid,
                    'legend': this.get$Container().find('legend').text().trim(),
                    'name': this.name
                });
            }
        });
        if (fsections.length > 0) {
            var i = 0;
            $(fsections).each(function () {
                reshtml += '<h3>' + this.legend + '</h3>';
                reshtml += '<ul>';
                $(Z('attr:'+this.name).first().f).each(function() { 
                    zAttr = Z('attr:' + this).first();
                    switch (zAttr.type) {
                        case 'inputText':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.getItem().text + '</li>';
                            }
                            break;
                        case 'textArea':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.val() + '</li>';
                            }
                            break;
                        case 'textBlock':
                            reshtml += zAttr.val();
                            break;
                        case 'inputDatePicker':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.getItem().text + '</li>';
                            }
                            break;
                        case 'checkBox':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                if(zAttr.val() === true){
                                    checkBoxValue = 'Ja'
                                }
                                else{
                                    checkBoxValue = 'Nej'
                                }
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + checkBoxValue + '</li>';
                            }
                            break;
                        case 'RadioButton':
                        case 'rtfTextArea':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.getItem().text + '</li>';
                            }
                            break;
                        case 'dropDownList':
                            if ($('#' + $('#' + zAttr.elementid).parent().attr('id')).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.val() + '</li>';
                            }
                            break;
                        case 'dynamicDropDownList':
                            if ($('#' + $('#' + zAttr.elementid).parent().attr('id')).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ';
                                if (zAttr.get$Element().data().select2.opts.multiple) {
                                    if (zAttr.val().length > 1) {
                                        for (j = 0; j < zAttr.getItems().length; j++) {
                                            if (zAttr.getItems()[j].isSelected) {
                                                 reshtml += zAttr.getItems()[j].text + ', ';
                                            }
                                        }
                                        reshtml = reshtml.substr(0, reshtml.length - 2) + '</li>';
                                    } else if (zAttr.val().length == 1) {
                                        reshtml += zAttr.getItem().text + '</li>';
                                    }
                                    else {
                                        reshtml += '</li>';
                                    }
                                }
                                else {
                                    reshtml += zAttr.getItem().text + '</li>';
                                }
                            }
                            break;
                        case 'unrestrictedDropDownList':
                            if ($('#' + $('#' + zAttr.elementid).parent().attr('id')).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ';
                                if (zAttr.isMultiple) {
                                    if (zAttr.val().length > 1) {
                                        for (j = 0; j < zAttr.getItems().length; j++) {
                                            if (zAttr.getItems()[j].isSelected) {
                                                 reshtml += zAttr.getItems()[j].text + ', ';
                                            }
                                        }
                                        reshtml = reshtml.substr(0, reshtml.length - 2) + '</li>';
                                    } else if (zAttr.val().length == 1) {
                                        reshtml += zAttr.getItem().text + '</li>';
                                    }
                                    else {
                                        reshtml += '</li>';
                                    }
                                }
                                else {
                                    reshtml += zAttr.getItem().text + '</li>';
                                }
                            }
                            break;
                        case 'serviceSelector':
                            if ($('#' + $('#' + zAttr.elementid).parent().attr('id')).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ';
                                if (zAttr.isMultiple) {
                                    if (zAttr.val().length > 1) {
                                        for (j = 0; j < zAttr.getItems().length; j++) {
                                            if (zAttr.getItems()[j].isSelected) {
                                                 reshtml += zAttr.getItems()[j].text + ', ';
                                            }
                                        }
                                        reshtml = reshtml.substr(0, reshtml.length - 2) + '</li>';
                                    } else if (zAttr.val().length == 1) {
                                        reshtml += zAttr.getItem().text + '</li>';
                                    }
                                    else {
                                        reshtml += '</li>';
                                    }
                                }
                                else {
                                    if (zAttr.getItem().text.length > 0) {
                                        reshtml += zAttr.getItem().text + '</li>';
                                    } else {
                                        reshtml += 'Nej</li>';                                    
                                    }
                                }
                            }
                            break;
                        case 'default':
                            if ($('#' + zAttr.elementid).is(':visible')) {
                                reshtml += '<li><b>' + zAttr.displayName() + '</b>: ' + zAttr.getItem().text + '</li>';
                            }
                            break;
                    }
                });
                reshtml += '</ul>';
            });
        }
    }    
    return reshtml;
}

Instead do this...

function getOrderDetails(showRealValues) {

    var formSections = Z('attr:[type=fieldSet]');
    var reshtml = '';

    formSections.each(function(i) {
        var section = this;
        reshtml += '<h3>' + section.displayName().trim() + '</h3>\n';
        reshtml += '<ul>\n';

        Z(section.children()).each(function (j) {
            if (this.visible()) {
                reshtml += '\t<li><b>' + this.displayName() + '</b>';

                if (this.isMultiple) {
                    var items = this.getItems();
                    var selectedItems = [];
                    Z(items).each(function() { if (this.isSelected) { selectedItems.push(this.text); } });
                    reshtml += ': ' + selectedItems.join(', ');
                } else {
                    var item = this.getItem();
                    if (item.text != null) {
                        var value = typeof item.value === "boolean" ? item.value ? "Yes" : "No" : item.value != null ? item.value : '';
                        var text = item.text.length > 0 ? item.text : value;
                        reshtml += ': ' + text + (showRealValues ? ' (' + value + ')' : '');
                    } else {
                        reshtml += this.val();
                    }
                }
                reshtml += '</li>\n';
            }
        });
        reshtml += '</ul>';
    });

    return reshtml;
}

Z('#orderDetailsHtml').submit(function(){
    Z('#orderDetailsHtml').val(getOrderDetails());
});

Validation for personal number

Don't do this...

window.luhn;
luhn = function(number) {
    var k, sum, v, _i, _len, _ref;
    number = "" + number;
    sum = 0;
    _ref = number.split('');
    for (k = _i = 0, _len = _ref.length; _i < _len; k = ++_i) {
        v = _ref[k];
        v *= 2 - (k % 2);
        if (v > 9) {
            v -= 9;
        }
        sum += v;
    }
    return Math.ceil(sum / 10) * 10 - sum;
};

$.validator.addMethod("personnummer", function(value, element) {
    var cd, control, day, divider, month, p, serial, year, _ref;
    value = "" + value;
    p = value.match(/^(18|19|20|21){1}(\d{2})(\d{2})(\d{2})([\-\+]{1})(\d{3})(\d{0,1})$/);
    if (!p) {
        return false;
    }
    _ref = p.slice(2), year = _ref[0], month = _ref[1], day = _ref[2], divider = _ref[3], serial = _ref[4], control = _ref[5];
    cd = luhn("" + year + month + day + serial);
    return cd === +control && !!control;
}, "Ogiltigt personnummer");

Instead do this...

window.pnr = false;
window.luhn;
luhn = function(number) {
    var k, sum, v, _i, _len, _ref;
    number = "" + number;
    sum = 0;
    _ref = number.split('');
    for (k = _i = 0, _len = _ref.length; _i < _len; k = ++_i) {
        v = _ref[k];
        v *= 2 - (k % 2);
        if (v > 9) {
            v -= 9;
        }
        sum += v;
    }
    return Math.ceil(sum / 10) * 10 - sum;
};

function personalNumberValidation(value) {
    var cd, control, day, divider, month, p, serial, year, _ref;
    value = "" + value;
    p = value.match(/^(18|19|20|21){1}(\d{2})(\d{2})(\d{2})([\-\+]{1})(\d{3})(\d{0,1})$/);
    if (!p) {
        return false;
    }
    _ref = p.slice(2), year = _ref[0], month = _ref[1], day = _ref[2], divider = _ref[3], serial = _ref[4], control = _ref[5];
    cd = luhn("" + year + month + day + serial);
    console.log(cd === +control && !!control);
    window.pnr = cd === +control && !!control;
    return pnr;
};

Z('#personalIdentityNumber').change(function(){
    personalNumberValidation(Z('#personalIdentityNumber').val());
    Z('#personalIdentityNumber').valid(pnr,"Felaktigt personnummer."); 
});

Dynamic required field

Don't do this...

Z('#Field').first().get$Element().rules('add','required');
Z('#Field').first().get$Element().rules('remove','required');

Instead do this...

Z('#Field').require(true)
Z('#Field').require(false)