This post is part of a series about auto-tagging
In this article I will describe how to create your views for your Adobe Launch extension. Extensions are a way to extend your tagging implementation to provide reusable content for your tagging team to use in all your Adobe Launch containers.
When building a private or public extension, you will be given the option to provide a configuration view for your extension or extension feature. While it is optional to provide a configuration, I will advise you to always choose this option.
As a best practice it is advised to add a view when creating a data element or a condition or a rule or an extension. Even if you are not planning to return a settings object, a view should be used to provide additional details about the feature.
Going forward I will consider that you have already ran the following command and answered yes to the question about the configuration view.
npx @adobe/reactor-scaffold
When we first generate the scaffold of our extension you would notice that the src folder is automatically generated for us. Inside this folder you will find the lib and view folder.
The view folder should contain all the HTML files which will be used in the Adobe Launch UI. When an extension is installed, Adobe Launch will allow you to use the actions, data elements etc... that you configured in your extension.json file. When you select in the Adobe Launch interface one of the features that you configured, Adobe Launch will fetch the corresponding HTML file and load it in an iframe. The main purpose of a view is to return a settings object that will then be used in the JavaScript file present in the lib folder.
All files in the lib folder will be present in the Adobe Launch core library that you load on your website.
Try to remove the .min from your Adobe Launch library URL and have a look. You will see the content of your js files inside it.
It is important to note that for the views to work you need to put the CSS files and any other helper files inside the view folder itself. This way when you will use npx @adobe/reactor-packager command, they will automatically be zipped.
By default, the scaffold command will not add any CSS to your HTML file. It is up to you to use any CSS framework that suits you best (hence the great flexibility of the extensions). This means that you can customize your extension to match your brand design.
Please note that while some public extensions available in Adobe Launch use Single Page Application framework like React to create the Views, you do not have to use any. I use default HTML, CSS and Vanilla JavaScript and it meets all my requirements.
The default HTML code will look as follow in your HTML files:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Extension Configuration</title> </head> <body> Extension Configuration Template <script src="https://assets.adobedtm.com/activation/reactor/extensionbridge/extensionbridge.min.js"></script> <script> window.extensionBridge.register({ init: function(info) { if (info.settings) { // TODO Populate form values from persisted settings. } }, getSettings: function() { // TODO Return settings object from form values. }, validate: function() { // TODO Return whether settings are valid. } }); </script> </body> </html>
In the example above, you will see Extension Configuration Template. This section can be replaced by any HTML code that you want. You can style as you want as well. Usually, you will place your configuration form in this section.
You will see 2 script tags. Do not delete them. The first one is about the extensionbridge and needs to be present.
In the next script tag, you will find:
validate
In this section you will need to create your logic to validate that the configuration values provided match the minimum requirements. You need to check if required fields have a value and if specific fields contain right values. You should always return a Boolean. true should be returned if validation is a success and false should be returned if validation is a failure.
validate: function() { return true; }
getSettings
This function should be used to return the settings object which contains the configuration that will be used in the js file for the rule or data element or condition or extension.
getSettings: function() { return { test1: getValueFromField1(), test2: 123, test3: [1, 3] } },
You should not add any validation logic inside the getSettings section. The validate section should be used instead. Also, you should always have corresponding validation logic inside your js file to make sure the settings object is as you would expect.
init
This is called when the view is loaded. It checks if an existing settings object exist. If it exists, you can then prepopulate the form with the existing configuration.
init: function(info) { if (info.settings) { if (info.settings.test1) { test1Field.value = info.settings.test1; } } },
If your configuration view should not return any settings then you can replace it by this
window.extensionBridge.register({ init: function(info) {}, getSettings: function() { return {}; }, validate: function() { return true; } });
To upload an extension, you will first need to package it. Luckily for us Adobe already provides a command for this
npx @adobe/reactor-packager
For our CSS and helper JavaScript files to be part of the packager output zip file, we will need to reference them in the view folder. If they are outside there will not be available when the view HTML files loads in Adobe Launch.
You can also use CDN to deliver your helpers and CSS files. As the HTML file is loaded in an iframe, it should be safe to consider that any risks related to CDN should be mitigated.
For our example we will add Bulma CSS framework, ION Icons and some custom JavaScript helper files.
Add the file viewHelpers.js inside the js/helpers folder
//Support selection of data element function addListenerToDataElement() { var dataElementSelectorElements = document.querySelectorAll('.data-element'); if (dataElementSelectorElements) { dataElementSelectorElements.forEach(function (element) { element.addEventListener('click', function (event) { window.extensionBridge.openDataElementSelector().then(function (dataElement) { var target = event.target; while (!target.classList.contains('field')) { target = target.parentElement; } target.querySelector('.input').value = dataElement; }); }); }) } } addListenerToDataElement(); //used when missing field function toggleNotification() { document.querySelector('#error').classList.toggle('is-hidden'); }
The folder structure should now look as follow
> src > view > actions > dataElements > configuration > events > css bulma.min.js > js > helpers viewHelpers.js
Go to your html files and update them to reference your CSS file and JavaScript files
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Extension Configuration</title> <link rel="stylesheet" href="../css/bulma.min.css"> <script src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons.js"></script> </head> <body> <section class="section is-hidden" id="error"> <div class="notification is-danger is-light"> <button class="delete" onclick="toggleNotification()"></button> <ion-icon name="alert" class="has-text-danger"></ion-icon> Error message </div> </section> <script src="https://assets.adobedtm.com/activation/reactor/extensionbridge/extensionbridge.min.js"></script> <script> window.extensionBridge.register({ init: function (info) { }, getSettings: function () { return {}; }, validate: function () { return true; } }); </script> <script src="../js/helpers/viewHelpers.js"></script> </body> </html>
The view HTML file will allow you to return a settings object with the configuration to be used when the action or data element or condition or extension is executed.
There is different ways to provide settings:
We are now going to add to our HTML file the structure of our form.
We will use the openDataElementSelector and openCodeEditor shared views to edit our code.
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Extension Configuration</title> <link rel="stylesheet" href="../css/bulma.min.css"> <script src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons.js"></script> </head> <body> <section class="section is-hidden" id="error"> <div class="notification is-danger is-light"> <button class="delete" onclick="toggleNotification()"></button> <ion-icon name="alert" class="has-text-danger"></ion-icon> Error message </div> </section> <section> <!--Normal field--> <label class="label">Normal Field</label> <div class="field"> <div class="control is-expanded"> <input id="normal" class="input" type="text" placeholder="Input a value"> </div> </div> <!--Field with Data Element selection--> <label class="label">Data Element Selection field</label> <div class="field has-addons"> <div class="control is-expanded"> <input id="dataElement" class="input" type="text" placeholder="Click on icon to select your data element"> </div> <div class="control"> <a class="button is-info data-element"> <span class="icon is-small"> <ion-icon name="reorder"></ion-icon> </span> </a> </div> </div> <!--Button to edit JSON config--> <button class="button is-medium is-outlined is-info" id="edit-config" onClick="editConfig()"> <span class="icon is-large is-primary"> <ion-icon name="code"></ion-icon> </span> <span>Edit Config</span> </button> <!--Button to edit custom code--> <button class="button is-medium is-outlined is-info" id="edit-function" onClick="editFunction()"> <span class="icon is-large is-primary"> <ion-icon name="code"></ion-icon> </span> <span>Edit Function</span> </button> </section> <script> /** * Will open a code editor to edit the JSON value */ function editConfig() { window.extensionBridge.openCodeEditor({ code: window.config.source, language: 'json' }).then(function (code) { window.config.source = code; }); } /** * Will open a code editor to edit the custom code */ function editFunction() { window.extensionBridge.openCodeEditor({ code: window.customCode.source, language: 'javascript' }).then(function (code) { window.customCode.source = code; }); } </script> <script src="https://assets.adobedtm.com/activation/reactor/extensionbridge/extensionbridge.min.js"></script> <script> window.customCode = { source: '' } //Stores custom code to be displayed in code editor window.config = { source: '' } //Stores custom JSON config to be displayed in code editor var normal = document.querySelector('#normal'); var dataElement = document.querySelector('#dataElement'); window.extensionBridge.register({ // We need to remember to prepopulate the config forms so we can edit saved data init: function (info) { if (info.settings) { if (info.settings.normal) { normal.value = info.settings.normal; } if (info.settings.dataElement) { dataElement.value = info.settings.dataElement; } if (info.settings.config) { window.config.source = info.settings.config; } if (info.settings.customCode) { window.customCode.source = info.settings.customCode; } } }, //We will return a custom object that will be available in the settings object during runtime getSettings: function () { return { normal: normal.value, dataElement: dataElement.value, config: window.config.source, customCode: window.customCode.source }; }, validate: function () { return true; } }); </script> <script src="../js/helpers/viewHelpers.js"></script> </body> </html>
For the function part, we will also need to specify the property tranforms in the extension.json file. In our case we want our custom code to be stored in a function so that we can execute it. By default, any custom code using the code editor will be stored as a String in the settings object.
Inside extension.json add transforms property for the view:
"configuration": { "viewPath": "configuration/configuration.html", "schema": { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": {} }, "transforms": [{ "type": "function", "propertyPath": "customCode", "parameters": ["window", "anotherArgument", "anotherOne"] }] },
Notice that you can specify more than one transform action. In our case we are using a type of function on the settings property customCode. We also specify that the function can have 3 arguments when it is called.
Once the extension is uploaded, navigate to the extension or rule or data element or condition that you configured previously. You should see the form that you created. When you save the changes, the settings object will be saved when the main Adobe Launch library has been built.
Check the Adobe Launch library URL without the .min and you should see how the settings object is saved.
When you create an action or data element or condition or extension configuration view using the scaffold command, you will notice that:
Notice that by default the configuration view for the extension will not generate a js file. You will need to create one and reference it in the extension.js file in the main property.
In the js file you will see that it would have generated some skeleton code.
'use strict'; module.exports = function(settings) { // TODO Perform some action. };
Conditions also have a trigger argument.
Please make sure to always update the arguments to contain event. Adobe Launch has it owns event object which can be leveraged in your code. It provides details about what type of rule is being used to trigger this action but it also provides event.detail which contains the metadata provided as payload when a direct call rule is triggered.
_satellite.track('myDCR', { test: 123, prop1: ['a', 'b']})
Without adding the event argument we will lose this payload. Even if you are not planning to use the event object, it is still a best practice to reference it. If you omit it, any data element which rely on the event object will not return the correct data. So you reference it for other features to use it.
You can call a data element in the code using _satellite.getVar('myDataElement', event) and in the data element view you can leverage the event object using %event.detail.test%
Now your code should look as follow:
'use strict'; module.exports = function(settings, event) { // TODO Perform some action. };
As mentioned, the settings saved in the extension configuration view are not saved in a settings object passed as argument to a function. You will need to use turbine.getExtensionSettings() in your extension code to leverage the extension settings. This can be used in any JavaScript file of your extension.
I have now described how to create a view for your extension and use the settings configured to use in your code. Developing your own private extension will allow you to save a lot of time while tagging.
It is also in line with the DRY principle which is part of our tagging and coding standards
Views
Replies
Total Likes
Views
Likes
Replies
Views
Likes
Replies
Views
Likes
Replies