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)