Tutorial

Change listeners

„With great power comes great responsibility“

How to define a change listener

cplaceJS change listeners are defined in a type's cplaceJS settings which can be found next to its "attribute" tab since 4.55. They are a mighty tool when modeling prototypes or fixing small issues between releases without the need of deployments. As nearly all of cplaceJS, change listeners may also induce unwanted behaviour and hindrance to pro-code logic if handled carelessly, so awareness is advised.

Note that change listeners are called upon creation of a page only if an observed attribute was changed, i.e. there was a value assigned.

Defining a simple change listener

For our example we will implement a simple listener to calculate the margin of an article from its production costs and selling price. We've got three attributes:

  • cf.cplace.cost is a number which holds an article's production costs,
  • cf.cplace.price which is a number to hold the selling price of the article and
  • cf.cplace.margin where the margin of the article will be stored.

Upon change of the cost or price we want to adjust the calculated margin.

Navigate to the type definition and open the cplaceJS panel, then click on the "New change listener" button. In the modal enter a name for your change listener (we took "price calculation" here as this is what the listener shall do) and select the cost and price attributes as the triggering attributes.

Now for the actual logic: we want the listener to retrieve the new value for the cost and/or price attribute after the change. Luckily the changeEvent provided to the script lets us access the changed page via changeEvent.getEntity() where we can get the attributes from using the get method of the page api. So our script should look like this (with some comments for explanation):

// get the changed page
var page = changeEvent.getEntity();
// get the attributes from the page
var costs = page.get('cf.cplace.cost');
var price = page.get('cf.cplace.price');

Then we check for null values in case the article doesn't have a price or cost yet (or someone forgot to provide it) and do the calculation of the margin (which luckily is quite simple in our case):

var margin = 0.00;
if (costs === null) {
  costs = 0.00;
}
if(price === null) {
    price = 0.00;
}
margin = price - costs;

Now we apply the calculated margin to the attribute using the updatePage action of the platform. We will pass it the page and an option object which only contains the new attribute information:

cplace.actions().updatePage(page, {
  customAttributes: {
    'cf.cplace.margin': margin
  }
});

We're almost done now. Finally tell the attribute to refresh after it has been set, so the user has some immediate feedback. Do this using registerAttributeForRefresh() on the updated page:

page.registerAttributeForRefresh('cf.cplace.margin');

Small note on the GroupedAttributes widgets: cplace will only refresh those attributes which have the Allow in-place editing option enabled - regardless of whether the attribute is read only or not.

Finally hit the save button after that and we're done with our change listener and can run a first test. Our final result now should look like that:

var page = changeEvent.getEntity();
var costs = page.get('cf.cplace.cost');
var price = page.get('cf.cplace.price');
var margin = 0.00;

if (costs === null) {
  costs = 0.00;
}
if(price === null) {
    price = 0.00;
}
margin = price - costs;

cplace.actions().updatePage(page, {
  customAttributes: {
    'cf.cplace.margin': margin
  }
});
page.registerAttributeForRefresh('cf.cplace.margin');

Note that when writing complex change listeners you cannot use searches due to platform limitations.