HomeChevron rightBlogChevron right
Marketplace
Marketplace
(read time)

Entwicklung maßgeschneiderter Lösungen mit ElabNext-Add-Ons

A short guide to developing custom add-ons for eLabNext, covering essential steps such as enabling Developer mode and utilising SDK and API methods.

A laboratory

Download Whitepaper

By submitting this form, you agree with our Privacy Policy.
Thank you! Download the file by clicking below:
Download
Oops! Something went wrong while submitting the form.

Table of Contents

Publish Date

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Table of Contents

AOByte recently started a new partnership with eLabNext, an all-in-one lab management software. eLabNext solutions help to improve the quality of the research by providing all-around tools for any lab. Due to its rapid expansion, eLabNext has decided to release an SDK, allowing developers to create new add-ons that other users can install on the eLabNext dashboard. Add-ons allow users to integrate 3rd party software into dashboards, software such as Dropbox, Google Drive, etc. Add-ons also enable users to add functionality to their dashboard without waiting for their desired functionality to be released by eLabNext.

Our company is proud to accompany eLabNext on its journey. Seeing a growing demand, we’ve decided to share part of our journey of creating custom add-ons. This article is a good place to start if you’re interested in eLabNext add-on development.

Starting Add-on Development

To start add-on development, you must first turn on Developer mode from settings. Navigate to Account Settings > Developer. Developer mode is turned on by simply toggling the switch. In turned-on Developer mode, the SDK will attempt to inject an add-on JavaScript file from the “Add-on script URL” on the page load. A single JavaScript file will be loaded at runtime on page load each time you browse the eLabNext dashboard.

Now, let’s try to create a simple add-on. Before jumping into coding, here are two valuable resources: eLabNext SDK documentation and eLabNext REST API documentation.

Use the Download Template from the Developer settings page to create an empty add-on. This is a working sample add-on, which can be fed to the SDK via an HTTP server of your choice. Our team is using a NodeJS-based http-server for development purposes. The add-on below achieves a simple task of displaying the tasks table in the dashboard. It also allows users to create and delete tasks.

/*

@rootVar: SAMPLE_ADDON

@name: Sample

@description: Sample addon

@author: Stepan Smbatyan

@version: 1.0.0

*/

var SAMPLE_ADDON = {};



((context) => {

context.init = (config) => {

$(() => {

context.SampleAddon = new context.SampleAddon(config);

});

};



context.SampleAddon = new Class({

Implements: [Options, Events],

Extends: eLabSDK.Base,

options: {},

initialize: function (config) {

// Store a reference to the function's context

var self = this;

// Set the options for the application using the provided configuration

self.setOptions(config);



$(document).ready(() => {

const currentPage = Helper.History.get('pageID');



const pageID = currentPage || new URLSearchParams(window.location.search).get('pageID');



renderTaskPage();



if (pageID === 'tasks') {

getTasks().then(({ data }) => {

renderTaskTable(data);



addDeleteBtnListener();

});

}

});

},

});



// #TODO: remove context.init() when upload as add-on to marketplace

context.init();

})(SAMPLE_ADDON);



// ======================================= DOM =======================================



/**

* Renders the task list UI by updating the browser history, creating a button and table,

* filling the table with task data, and updating the main content section with the table container.

* @param {Event} e - Optional event object. If provided, prevents the default action.

*/

const renderTaskTable = (data) => {

const button = createAddTaskButton();

$('#main-content')

.html('<section id="tableContainer"></section>')

.prepend(button.render());



const table = createTaskTable();

table.data = data;

table._renderHTML();

};



/**

* Creates a custom page for tasks using eLabSDK.

* This function initializes a new CustomPage object with specified configurations.

* @returns {CustomPage} A CustomPage object representing the task page.

*/

const renderTaskPage = () => {

return new eLabSDK.CustomPage({

rootVar: '.nav-main-level',

pageID: 'tasks',

mainMenu: 'Tasks',

subMenu: 'Task list',

});

};



/**

* Creates a button element using the eLabSDK.GUI.Button constructor.

* The button is configured with a label, CSS class,

* and an action to show a dialog for updating tasks.

* @returns {eLabSDK.GUI.Button} - A button element configured to add a new task when clicked.

*/

const createAddTaskButton = () => {

return new eLabSDK.GUI.Button({

label: 'Add New Task',

class: 'addNewTaskBtn',

action: () => showDialog(DIALOG_CONFIGS.CREATE, createTaskAction),

});

};



const addDeleteBtnListener = () => {

$('.deleteBtn').on('click', (e) => {

const id = e.currentTarget.getAttribute('_dataId');



showDialog(DIALOG_CONFIGS.DELETE, () => deleteTaskAction(id));

});

};



/**

* Creates a table element using the Helper.Table.create method.

* The table is configured with specified target container, data

* and columns for displaying task information.

* @returns {HTMLElement} - A table element configured to display task information.

*/

const createTaskTable = () => {

return Helper.Table.create({

target: 'tableContainer',

caption: null,

data: {},

columns: [

{

name: 'Full Name',

key: 'fullName',

width: '20%',

cellRender: ({ creator }) => `<b>${creator.fullName}</b>`,

},

{

name: 'Title',

key: 'title',

width: '20%',

cellRender: ({ title }) => `<span>${title || '-'}</span>`,

},

{

name: 'Description',

key: 'contents',

width: '45%',

cellRender: ({ contents }) => `<span>${contents || '-'}</span>`,

},

{

name: 'Created',

key: 'created',

width: '10%',

cellRender: ({ created }) => `<span>${created.split('T')[0]}</span>`,

},

{

name: 'Action',

key: 'actions',

width: '5%',

cellRender: ({ taskID }) => `

<p class='deleteTranslationIcon deleteBtn' _dataId="${taskID}">

<i class='fa fa-trash-alt _actionIcon' title='Delete translation'></i>

</p>

`,

},

],

});

};



// ======================================= MODAL =======================================



/**

* Initiates the deletion of a task identified by its taskId asynchronously.

* Upon successful deletion, closes any open dialogs, reloads the page to reflect the changes.

* @param {string} taskId - The ID of the task to be deleted.

* @returns {Promise<void>} - A Promise that resolves after the task deletion and page reload.

*/

const deleteTaskAction = async (taskId) => {

await deleteTask(taskId);

Dialog.closeWait();

window.location.reload();

};



/**

* Adding a new task with the provided title and description,

* closing the dialog window, and reloading the current page.

* @returns {Promise<void>} A promise that resolves once the actions are updated.

*/

const createTaskAction = async () => {

const title = $('#title').val();

const description = $('#description').val();



await addTask({ title, description });

Dialog.closeWait();

window.location.reload();

};



/**

* Displays a dialog window with specified configuration options and a custom button,

* calling the provided callback function when the custom button is clicked.

*

* @param {Object} config - The configuration object for the dialog window.

* @param {string} config.title - The title of the dialog window.

* @param {number} config.width - The width of the dialog window.

* @param {string} config.btnOk - The label for the OK button.

* @param {string} config.btnCancelLabel - The label for the Cancel button.

* @param {string} config.content - The content to be displayed in the dialog window.

* @param {string} config.customButtonLabel - The label for the custom button.

* @param {string} config.customButtonStyle - The style for the custom button.

* @param {Function} callback - The callback function to be called when the custom button is clicked.

* @returns {void}

*/

const showDialog = (config, callback) => {

const {

title,

width,

btnOk,

btnCancelLabel,

content,

customButtonLabel,

customButtonStyle,

} = config;



Dialog.show({

title,

width,

btnOk,

btnCancelLabel,

content,

customButtons: [

{

label: customButtonLabel,

style: customButtonStyle,

fn: callback,

},

],

});

};



// ======================================= CONSTANTS =======================================



const DIALOG_CONFIGS = {

DELETE: {

title: 'Delete Task',

width: '550',

btnOk: false,

btnCancelLabel: 'Close',

content: '<p>Are you sure you want to delete this task?</p>',

customButtonLabel: 'Delete Task',

customButtonStyle: 'background:#fe810',

},

CREATE: {

title: 'Add New Task',

width: '550',

btnOk: false,

btnCancelLabel: 'Close',

content: `

<section>

<input id="title" type="text" placeholder="Title" />

<textarea id="description" placeholder="Description" style="padding-top: 8px;"/>

</section>

`,

customButtonLabel: 'Add Task',

customButtonStyle: 'background:#fe810',

},

};





// ======================================= API =======================================



/**

* Retrieves tasks by making a GET request to eLabSDK.

*

* @returns {Promise<Array>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const getTasks = () => new Promise((resolve, reject) => {

new eLabSDK.API.Call({

method: 'GET',

path: 'tasks',

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute();

});



/**

* Adds a new task with the provided title and description by making a POST request to eLabSDK.

*

* @param {Object} task - An object containing the title and description of the task.

* @param {string} task.title - The title of the task.

* @param {string} task.description - The description of the task.

* @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const addTask = ({ title, description }) => new Promise((resolve, reject) => {

const data = {

assigneeID: 0,

title,

contents: description,

};



new eLabSDK.API.Call({

method: 'POST',

path: 'tasks',

pathParams: {},

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute(data);

});



/**

* Deletes a task with the specified ID by making a DELETE request to eLabSDK.

*

* @param {string} id - The ID of the task to be deleted.

* @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const deleteTask = (id) => new Promise((resolve, reject) => {

new eLabSDK.API.Call({

method: 'DELETE',

path: `tasks/${id}`,

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute();

});

Eines der wichtigsten Dinge, die Sie bei der Erstellung eines Add-ons beachten sollten, ist die Priorisierung der Verwendung von SDK- und API-Methoden gegenüber benutzerdefiniertem Code. Ein gutes Beispiel ist das Rendern von Schaltflächen oder das Stellen von HTTP-Anfragen. Wenn Sie die vom SDK bereitgestellten Methoden verwenden, können Sie sicher sein, dass z. B. die Schaltflächen das richtige Design haben oder dass alle erforderlichen Header an Ihre HTTP-Anfrage angehängt werden.

Komplexere Add-on-Entwicklung

Offensichtlich sind die meisten Add-Ons, die erstellt werden, komplizierter als dieses Beispiel. Natürlich möchten Entwickler bei der Bereitstellung komplexerer Funktionen die Möglichkeiten nutzen, Code in Module aufzuteilen, den Code für die Produktion zu minimieren, Testfälle für ihren Code zu schreiben und alle anderen Vorteile der modernen Webentwicklung zu nutzen. Während der Arbeit an den Add-Ons haben wir ein Boilerplate-Add-on erstellt, das es Benutzern ermöglicht, eine Projektstruktur, ein Paketieren, Testen usw. zu erstellen. Das Projekt finden Sie unter GitHub.

Denken Sie daran, dass das eLabNext SDK an Dynamik gewinnt; daher muss die Dokumentation vervollständigt werden. Bitte kontaktieren Sie unser Team, wenn Sie sich in einer Situation befinden, in der möglicherweise Hilfe benötigt wird. Unser Team wird weiterhin über den Entwicklungsprozess von eLabNext Add-Ons schreiben. Wir werden Themen wie die Einreichung von Add-Ons auf dem eLab Marketplace, Tipps und Tricks zur Entwicklung von eLabNext-Add-Ons, die Entwicklung komplizierterer Funktionen usw. behandeln.

AOByte recently started a new partnership with eLabNext, an all-in-one lab management software. eLabNext solutions help to improve the quality of the research by providing all-around tools for any lab. Due to its rapid expansion, eLabNext has decided to release an SDK, allowing developers to create new add-ons that other users can install on the eLabNext dashboard. Add-ons allow users to integrate 3rd party software into dashboards, software such as Dropbox, Google Drive, etc. Add-ons also enable users to add functionality to their dashboard without waiting for their desired functionality to be released by eLabNext.

Our company is proud to accompany eLabNext on its journey. Seeing a growing demand, we’ve decided to share part of our journey of creating custom add-ons. This article is a good place to start if you’re interested in eLabNext add-on development.

Starting Add-on Development

To start add-on development, you must first turn on Developer mode from settings. Navigate to Account Settings > Developer. Developer mode is turned on by simply toggling the switch. In turned-on Developer mode, the SDK will attempt to inject an add-on JavaScript file from the “Add-on script URL” on the page load. A single JavaScript file will be loaded at runtime on page load each time you browse the eLabNext dashboard.

Now, let’s try to create a simple add-on. Before jumping into coding, here are two valuable resources: eLabNext SDK documentation and eLabNext REST API documentation.

Use the Download Template from the Developer settings page to create an empty add-on. This is a working sample add-on, which can be fed to the SDK via an HTTP server of your choice. Our team is using a NodeJS-based http-server for development purposes. The add-on below achieves a simple task of displaying the tasks table in the dashboard. It also allows users to create and delete tasks.

/*

@rootVar: SAMPLE_ADDON

@name: Sample

@description: Sample addon

@author: Stepan Smbatyan

@version: 1.0.0

*/

var SAMPLE_ADDON = {};



((context) => {

context.init = (config) => {

$(() => {

context.SampleAddon = new context.SampleAddon(config);

});

};



context.SampleAddon = new Class({

Implements: [Options, Events],

Extends: eLabSDK.Base,

options: {},

initialize: function (config) {

// Store a reference to the function's context

var self = this;

// Set the options for the application using the provided configuration

self.setOptions(config);



$(document).ready(() => {

const currentPage = Helper.History.get('pageID');



const pageID = currentPage || new URLSearchParams(window.location.search).get('pageID');



renderTaskPage();



if (pageID === 'tasks') {

getTasks().then(({ data }) => {

renderTaskTable(data);



addDeleteBtnListener();

});

}

});

},

});



// #TODO: remove context.init() when upload as add-on to marketplace

context.init();

})(SAMPLE_ADDON);



// ======================================= DOM =======================================



/**

* Renders the task list UI by updating the browser history, creating a button and table,

* filling the table with task data, and updating the main content section with the table container.

* @param {Event} e - Optional event object. If provided, prevents the default action.

*/

const renderTaskTable = (data) => {

const button = createAddTaskButton();

$('#main-content')

.html('<section id="tableContainer"></section>')

.prepend(button.render());



const table = createTaskTable();

table.data = data;

table._renderHTML();

};



/**

* Creates a custom page for tasks using eLabSDK.

* This function initializes a new CustomPage object with specified configurations.

* @returns {CustomPage} A CustomPage object representing the task page.

*/

const renderTaskPage = () => {

return new eLabSDK.CustomPage({

rootVar: '.nav-main-level',

pageID: 'tasks',

mainMenu: 'Tasks',

subMenu: 'Task list',

});

};



/**

* Creates a button element using the eLabSDK.GUI.Button constructor.

* The button is configured with a label, CSS class,

* and an action to show a dialog for updating tasks.

* @returns {eLabSDK.GUI.Button} - A button element configured to add a new task when clicked.

*/

const createAddTaskButton = () => {

return new eLabSDK.GUI.Button({

label: 'Add New Task',

class: 'addNewTaskBtn',

action: () => showDialog(DIALOG_CONFIGS.CREATE, createTaskAction),

});

};



const addDeleteBtnListener = () => {

$('.deleteBtn').on('click', (e) => {

const id = e.currentTarget.getAttribute('_dataId');



showDialog(DIALOG_CONFIGS.DELETE, () => deleteTaskAction(id));

});

};



/**

* Creates a table element using the Helper.Table.create method.

* The table is configured with specified target container, data

* and columns for displaying task information.

* @returns {HTMLElement} - A table element configured to display task information.

*/

const createTaskTable = () => {

return Helper.Table.create({

target: 'tableContainer',

caption: null,

data: {},

columns: [

{

name: 'Full Name',

key: 'fullName',

width: '20%',

cellRender: ({ creator }) => `<b>${creator.fullName}</b>`,

},

{

name: 'Title',

key: 'title',

width: '20%',

cellRender: ({ title }) => `<span>${title || '-'}</span>`,

},

{

name: 'Description',

key: 'contents',

width: '45%',

cellRender: ({ contents }) => `<span>${contents || '-'}</span>`,

},

{

name: 'Created',

key: 'created',

width: '10%',

cellRender: ({ created }) => `<span>${created.split('T')[0]}</span>`,

},

{

name: 'Action',

key: 'actions',

width: '5%',

cellRender: ({ taskID }) => `

<p class='deleteTranslationIcon deleteBtn' _dataId="${taskID}">

<i class='fa fa-trash-alt _actionIcon' title='Delete translation'></i>

</p>

`,

},

],

});

};



// ======================================= MODAL =======================================



/**

* Initiates the deletion of a task identified by its taskId asynchronously.

* Upon successful deletion, closes any open dialogs, reloads the page to reflect the changes.

* @param {string} taskId - The ID of the task to be deleted.

* @returns {Promise<void>} - A Promise that resolves after the task deletion and page reload.

*/

const deleteTaskAction = async (taskId) => {

await deleteTask(taskId);

Dialog.closeWait();

window.location.reload();

};



/**

* Adding a new task with the provided title and description,

* closing the dialog window, and reloading the current page.

* @returns {Promise<void>} A promise that resolves once the actions are updated.

*/

const createTaskAction = async () => {

const title = $('#title').val();

const description = $('#description').val();



await addTask({ title, description });

Dialog.closeWait();

window.location.reload();

};



/**

* Displays a dialog window with specified configuration options and a custom button,

* calling the provided callback function when the custom button is clicked.

*

* @param {Object} config - The configuration object for the dialog window.

* @param {string} config.title - The title of the dialog window.

* @param {number} config.width - The width of the dialog window.

* @param {string} config.btnOk - The label for the OK button.

* @param {string} config.btnCancelLabel - The label for the Cancel button.

* @param {string} config.content - The content to be displayed in the dialog window.

* @param {string} config.customButtonLabel - The label for the custom button.

* @param {string} config.customButtonStyle - The style for the custom button.

* @param {Function} callback - The callback function to be called when the custom button is clicked.

* @returns {void}

*/

const showDialog = (config, callback) => {

const {

title,

width,

btnOk,

btnCancelLabel,

content,

customButtonLabel,

customButtonStyle,

} = config;



Dialog.show({

title,

width,

btnOk,

btnCancelLabel,

content,

customButtons: [

{

label: customButtonLabel,

style: customButtonStyle,

fn: callback,

},

],

});

};



// ======================================= CONSTANTS =======================================



const DIALOG_CONFIGS = {

DELETE: {

title: 'Delete Task',

width: '550',

btnOk: false,

btnCancelLabel: 'Close',

content: '<p>Are you sure you want to delete this task?</p>',

customButtonLabel: 'Delete Task',

customButtonStyle: 'background:#fe810',

},

CREATE: {

title: 'Add New Task',

width: '550',

btnOk: false,

btnCancelLabel: 'Close',

content: `

<section>

<input id="title" type="text" placeholder="Title" />

<textarea id="description" placeholder="Description" style="padding-top: 8px;"/>

</section>

`,

customButtonLabel: 'Add Task',

customButtonStyle: 'background:#fe810',

},

};





// ======================================= API =======================================



/**

* Retrieves tasks by making a GET request to eLabSDK.

*

* @returns {Promise<Array>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const getTasks = () => new Promise((resolve, reject) => {

new eLabSDK.API.Call({

method: 'GET',

path: 'tasks',

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute();

});



/**

* Adds a new task with the provided title and description by making a POST request to eLabSDK.

*

* @param {Object} task - An object containing the title and description of the task.

* @param {string} task.title - The title of the task.

* @param {string} task.description - The description of the task.

* @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const addTask = ({ title, description }) => new Promise((resolve, reject) => {

const data = {

assigneeID: 0,

title,

contents: description,

};



new eLabSDK.API.Call({

method: 'POST',

path: 'tasks',

pathParams: {},

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute(data);

});



/**

* Deletes a task with the specified ID by making a DELETE request to eLabSDK.

*

* @param {string} id - The ID of the task to be deleted.

* @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.

*/

const deleteTask = (id) => new Promise((resolve, reject) => {

new eLabSDK.API.Call({

method: 'DELETE',

path: `tasks/${id}`,

onSuccess: (xhr, status, response) => {

resolve(response);

},

onError: (xhr, status, response) => {

reject(response);

},

}).execute();

});

Eines der wichtigsten Dinge, die Sie bei der Erstellung eines Add-ons beachten sollten, ist die Priorisierung der Verwendung von SDK- und API-Methoden gegenüber benutzerdefiniertem Code. Ein gutes Beispiel ist das Rendern von Schaltflächen oder das Stellen von HTTP-Anfragen. Wenn Sie die vom SDK bereitgestellten Methoden verwenden, können Sie sicher sein, dass z. B. die Schaltflächen das richtige Design haben oder dass alle erforderlichen Header an Ihre HTTP-Anfrage angehängt werden.

Komplexere Add-on-Entwicklung

Offensichtlich sind die meisten Add-Ons, die erstellt werden, komplizierter als dieses Beispiel. Natürlich möchten Entwickler bei der Bereitstellung komplexerer Funktionen die Möglichkeiten nutzen, Code in Module aufzuteilen, den Code für die Produktion zu minimieren, Testfälle für ihren Code zu schreiben und alle anderen Vorteile der modernen Webentwicklung zu nutzen. Während der Arbeit an den Add-Ons haben wir ein Boilerplate-Add-on erstellt, das es Benutzern ermöglicht, eine Projektstruktur, ein Paketieren, Testen usw. zu erstellen. Das Projekt finden Sie unter GitHub.

Denken Sie daran, dass das eLabNext SDK an Dynamik gewinnt; daher muss die Dokumentation vervollständigt werden. Bitte kontaktieren Sie unser Team, wenn Sie sich in einer Situation befinden, in der möglicherweise Hilfe benötigt wird. Unser Team wird weiterhin über den Entwicklungsprozess von eLabNext Add-Ons schreiben. Wir werden Themen wie die Einreichung von Add-Ons auf dem eLab Marketplace, Tipps und Tricks zur Entwicklung von eLabNext-Add-Ons, die Entwicklung komplizierterer Funktionen usw. behandeln.

Read more of our blogs about modern lab management

Discover the latest in lab operations, from sample management to AI innovations, designed to enhance efficiency and drive scientific breakthroughs.

No items found.

Sind Sie bereit, Ihr Labor umzugestalten?

Verbessern Sie den Laborbetrieb, verbessern Sie die Zusammenarbeit und gewährleisten Sie die Datensicherheit mit eLabNext.

Häkchen-Symbol

Experiment management

Häkchen-Symbol

Inventory management

Häkchen-Symbol

Improved collaboration

Häkchen-Symbol

Verwaltung von Protokollen

Häkchen-Symbol

Workflow-Management für die Forschung

Eine Wissenschaftlerin

Melde dich für unseren Newsletter an

Holen Sie sich die neuesten Tipps, Artikel und exklusiven Inhalte zum modernen Labormanagement in Ihren Posteingang.
Danke! Deine Einreichung ist eingegangen!
Please check your email to verify your submission.
Hoppla! Beim Absenden des Formulars ist etwas schief gelaufen.