JS-Widget
Developing script.js
Let’s take a look at the general structure of script.js:
This part of the widget consists of the main required parts, which we will consider. Also, script.js can contain additional functions. Let’s analyze the initial frame of this file.
The entire widget is represented as an object. When the system loads widgets, it extends the existing Widget system object with the functionality described in script.js. In this way, the CustomWidget object inherits properties and methods that are useful for work and are parsed further. The object has callback functions that are called under certain conditions. These functions are listed in the table after the sample code script.js.
General view of script.js
define(['jquery'], function($){
var CustomWidget = function () {
var self = this, // to access an object from methods
system = self.system(), // This method returns an object with system variables.
langs = self.langs; // Localization object with data from the localization file (i18n folder)
this.callbacks = {
settings: function(){
},
init: function(){
return true;
},
bind_actions: function(){
return true;
},
render: function(){
return true;
},
dpSettings: function(){
},
advancedSettings: function() {
},
destroy: function(){
},
contacts: { selected: function() {
}
},
leads: { selected: function() {
}
},
onSave: function(){
}
};
return this;
};
return CustomWidget;
});
Callback functions, callbacks object
Function | Description |
---|---|
render: | When assembling the widget, callbacks.render is first called. This method usually describes the actions for displaying the widget. The widget will be displayed alone only in the settings menu, for displaying the widget in other areas, for example in the right column, you need to use special methods in this function , for example, the methods of the render () and / or render_template () object, which are parsed further. It is necessary that callbacks.render returns true. This is important, because without this, the callbacks.init and callbacks.bind_actions methods will not start. |
init: | Runs immediately after callbacks.render at the same time as callbacks.bind_actions. The init () method is usually used to collect necessary information and other actions, for example, communication with a third-party server and API authorization if the widget is used to send or request information to a third-party server. In the simplest In this case, it can, for example, determine the current location where the user is located. callbacks.init must return true for further work. |
bind_actions: | The callbacks.bind_actions method is used to hover events to actions taken by the user, for example, clicking a user on a button. callbacks.bind_actions must return true. |
settings: | The callbacks.settings method is called when the widget icon is clicked in the settings area. It can be used to add a modal window to the page, which is discussed in more detail below. The public widget should not hide / affect the rating and reviews of the widget. |
dpSettings: | The callbacks.dpSettings method is similar to callbacks.settings, but it is called in the scope of the digital_pipeline (more details Digital pipeline) |
advancedSettings: | The callbacks.advancedSettings method is called on the widget’s advanced settings page. For the functioning of this callback, you must specify the connection area of the widget advanced_settings. |
onSave: | callbacks.onSave is called when the user clicks the “Save” button in the widget’s settings. You can use to send the data entered into the form and change the status of the widget. |
leads:selected | This function is called when you select items from the list of leads, using the checkbox, and then click on the widget name in the extension menu. Used when you need to take any action with the selected objects. Examples are discussed below. |
contacts:selected | This function is called when you select items from the list of leads, using the checkbox, and then click on the widget name in the extension menu. Used when you need to take any action with the selected objects. Examples are discussed below. |
destroy: | This function is called when the widget is disabled through its settings menu. For example, you need to remove all widget elements from the DOM, if it was disabled, or take any further action. |
Example of the widget’s JS-code:
The following example demonstrates the use of an object callback functions with additional functions, as well as the use of some functions of the object widget. All these functions are discussed in the examples below. Let’s just look at this code, and for details, refer to the description of the functions of the widget object.
This widget will select from the contact list the marked contacts and transfer the phones and e-mail addresses to a third-party server.
The functions used in this example are discussed in more detail below. First of all, you should pay attention to the object callbacks.
define(['jquery'], function ($) {
var CustomWidget = function () {
var self = this,
system = self.system;
this.get_ccard_info = function () // Collecting information from a contact card
{
if (self.system().area == 'ccard') {
var phones = $('.card-cf-table-main-entity .phone_wrapper input[type=text]:visible'),
emails = $('.card-cf-table-main-entity .email_wrapper input[type=text]:visible'),
name = $('.card-top-name input').val(),
data = [],
c_phones = [], c_emails = [];
data.name = name;
for (var i = 0; i < phones.length; i++) {
if ($(phones[i]).val().length > 0) {
c_phones[i] = $(phones[i]).val();
}
}
data['phones'] = c_phones;
for (var i = 0; i < emails.length; i++) {
if ($(emails[i]).val().length > 0) {
c_emails[i] = $(emails[i]).val();
}
}
data['emails'] = c_emails;
console.log(data)
return data;
}
else {
return false;
}
};
this.sendInfo = function (person_name, settings) { // Sending collected information
self.crm_post(
'http://example.com/index.php',
{
// Sending POST data
name: person_name['name'],
phones: person_name['phones'],
emails: person_name['emails']
},
function (msg) {
},
'json'
);
};
this.callbacks = {
settings: function () {
},
dpSettings: function () {
},
init: function () {
if (self.system().area == 'ccard') {
self.contacts = self.get_ccard_info();
}
return true;
},
bind_actions: function () {
if (self.system().area == 'ccard' || 'clist') {
$('.ac-form-button').on('click', function () {
self.sendInfo(self.contacts);
});
}
return true;
},
render: function () {
var lang = self.i18n('userLang');
w_code = self.get_settings().widget_code; // in this case w_code='new-widget'
if (typeof(APP.data.current_card) != 'undefined') {
if (APP.data.current_card.id == 0) {
return false;
} // do not render contacts/add || leads/add
}
self.render_template({
caption: {
class_name: 'js-ac-caption',
html: ''
},
body: '',
render: '\
<div class="ac-form">\
<div id="js-ac-sub-lists-container">\
</div>\
<div id="js-ac-sub-subs-container">\
</div>\
<div class="ac-form-button ac_sub">SEND</div>\
</div>\
<div class="ac-already-subs"></div>\
// for public widgets
'<link type="text/css" rel="stylesheet" href="/widgets/' + w_code + '/style.css" >'
// for private widgets
'<link type="text/css" rel="stylesheet" href="/upl/widget/' + w_code + '/style.css" >'
});
return true;
},
contacts: {
selected: function () { // Here is the behavior for multi-select contacts and click on the name of the widget
var c_data = self.list_selected().selected;
$('#js-sub-lists-container').children().remove(); // The container is cleaned then the elements are collected in the container, selected in list.container - div block of widget, displayed in the right column.
var names = [], // Array of names
length = c_data.length; // Number of selected id (counting starts from 0)
for (var i = 0; i < length; i++) {
names[i] = {
emails: c_data[i].emails,
phones: c_data[i].phones
};
}
console.log(names);
for (var i = 0; i < length; i++) {
$('#js-ac-sub-lists-container').append('<p>Email:' + names[i].emails + ' Phone:' + names[i].phones + '</p>');
}
$(self.contacts).remove(); // clear the variable
self.contacts = names;
}
},
leads: {
selected: function () {
}
},
onSave: function () {
return true;
}
};
return this;
};
return CustomWidget;
});
The contents of the file new_widget.css, which can be in the folder with the widget
.card-widgets__widget-new_widget .card-widgets__widget__body {
padding: 0 10px 0px;
padding-bottom: 5px;
background-color: grey;
}
.ac-form {
padding: 5px 15px 15px;
margin-bottom: 10px;
background: #fff;
}
.js-ac-caption {
display: block;
margin: auto;
background-color : grey;
}
.lists_kommo_ac ul li span {
color: #81868f;
}
.ac-form-button{
padding: 5px 0;
background: #fafafb;
text-align: center;
font-weight: bold;
text-transform: uppercase;
border: 1px solid rgba(0, 0, 0, 0.09);
-webkit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.15);
box-shadow: 0 1px 0 0 rgba(0,0,0,0.15);
-webkit-border-radius: 2px;
border-radius: 2px;
font-size: 13px;
cursor: pointer;
}
.ac-form-button:active{
background: grey;
}
.ac-already-subs {
position: absolute;
width: 245px;
bottom: 10px;
right: 15px;
cursor: pointer;
color: #f37575;
background: #fff;
}
#js-ac-sub-lists-container, #js-ac-sub-subs-container {
min-height: 38px;
}
Widget object methods
The render () method
The render () method is used to work with template temigers twig.js, which is easy to use, you can see the documentation here.
The method is wrapper for twig.js and takes as parameters parameters for the template (data) and data for rendering this template (params). render (data, params). The method returns a rendered template. result = twig (data) .render (params).
Let’s take a simple example
var params = [
{name:'name1',
id: 'id1'},
{name:'name2',
id: 'id2'},
{name:'name3',
id: 'id3'}
]; // array of data sent for the template
var template = '<div><ul>'+
'{% for person in names %}'+
'<li>Name : {{ person.name }}, id: {{ person.id }}</li>'+
'{% endfor %}'+
'</ul></div>';
console.log(self.render({data : template},// submit template
{names: params}));
As a result, we get the markup:
-
Name: name1, id: id1
-
Name: name2, id: id2
-
Name: name3, id: id3
You can pass a function to one of the templates of our system, for this, in the transmitted data object you need to specify a reference to the template: ref: ‘/tmpl/controls/#TEMPLATE_NAME#.twig. For example, to create a drop-down list, use the existing template:
m_data = [
{option:'option1',
id: 'id1'},
{option:'option2',
id: 'id2'},
{option:'option3',
id: 'id3'}
]; // array of data sent for the template
var data = self.render(
{ref: '/tmpl/controls/select.twig'},// the data object in this case contains only a reference to the template
{
items: m_data, // Data
class_name:'subs_w', // class specification
id: w_code +'_list' // id specification
});
To look at the markup data, you need to add data to the DOM. The markup of the drop-down list is made in the style of our system.
To get acquainted with the full list of templates, you can follow the link. To use other system templates, you need to change the ref parameter, the general view: ref: ‘/tmpl/controls/#TEMPLATE_NAME#.twig’
-
textarea
-
suggest
-
select
-
radio
-
multiselect
-
date_field
-
checkbox
-
checkboxes_dropdown
-
file
-
button
-
cancel_button
-
delete_button
-
input
The render () method can transfer not only the system’s existing template references, but also links to its own templates. To do this, you pass a data object with a number of parameters. It is necessary to create a folder templates in the folder of our widget and put template template.twig in it. Consider an example
var params = {}; // empty data
var callback = function (template){ // callback function, called if the template is loaded, it is passed an object
template.
var markup = template.render(params); //
/*
* then the code to add markup to the DOM
*/
};
var s = self.render({
href:'templates/template.twig', // way to template
base_path: self.params.path; // The base way to the directory with the widget
load: callback // callback function will only occur if the template exists and is loaded
}, params); // parameters for the template
If the template exists at the link address, then the passed callback function is called, and the template object that contains the render method is passed to it, render parameters are rendered for rendering. In this example, the callback function call will occur if the template exists in the folder.
Example function for loading templates from the folder templates
For ease of reference, create a function. In it we will pass the parameters: template – the name of the template that lies in the folder with the widget in the template folder, params is the parameter object for the template, callbacks is the callback function that will be called after the template is loaded, in this case we will add the template to the modal window. For a modal window object, you can read in the JS section methods and objects for working with Kommo.
self.getTemplate = function (template, params, callback) {
params = (typeof params == 'object')?params:{};
template = template || '';
return self.render({
href:'/templates/' + template + '.twig',
base_path:self.params.path, // the widget will return to the object /widgets/#WIDGET_NAME#
load: callback // call a callback function
}, params); // parameters for the template
}
settings: function(){
self.getTemplate( // call the function
'login_block', // specify the name of the template that we have in the folder with the widget in the folder templates
{}, /* empty data for the template, because we first ask for a template, if it exists, then the callback function
call already
function to add data to the template, see below */
function(template) {
template.render({
widget_code:self.params.widget_code,// parameters for the template.
lang:self.i18n('settings')}));
}};
The render_template () method
The render_template () method wraps the markup or template passed to it into a standard widget (markup) and places the resulting markup in the right column of widgets
You can pass this markup to a given html function or a template with the data to render, as well as in the case of the render () method.
The function supplements the markup of its widget stored in the template_element variable passed to it.
/*
* html_data stores the markup that needs to be placed in the right column of widgets.
*/
var html_data ='<div class="nw_form">'+
'<div id="w_logo">'+
'<img src="/widgets/new_widget/images/logo.png" id="firstwidget_image"></img>'+
'</div>'+
'<div id="js-sub-lists-container">'+
'</div>'+
'<div id="js-sub-subs-container">'+
'</div>'+
'<div class="nw-form-button">BUTTON</div></div>'+
'<div class="already-subs"></div>';
self.render_template(
{
caption:{
class_name:'new_widget', // class name for the markup wrapper
},
body: html_data,// markup
render : '' // template is not sent
}
);
The simplest example was shown without using a template, but the render_template () method can also take a template and data for a template as parameters. You can also pass a link to a template, similar to the render () method.
/*
* Here, the template and data for the template are passed as parameters.
*/
var render_data ='<div class="nw_form">'+
'<div id="w_logo">'+
'<img src="/widgets/{{w_code}}/images/logo.png" id="firstwidget_image"></img>'+
'</div>'+
'<div id="js-sub-lists-container">'+
'</div>'+
'<div id="js-sub-subs-container">'+
'</div>'+
'<div class="nw-form-button">{{b_name}}</div></div>'+
'<div class="already-subs"></div>';
self.render_template(
{
caption:{
class_name:'new_widget'
},
body:'',
render : render_data
},
{
name:"widget_name",
w_code:self.get_settings().widget_code,
b_name:"BUTTON" // in this case it's better to pass a reference to lang via self.i18n ()
}
);
Get in the right column of the widget, created from the template.
The widget object has a number of useful functions that can be called to solve different tasks.
Description and examples are given below.
Function set_lang ()
The set_lang () function allows you to change the default settings for files from the i18n folder
The current lang object is stored in the langs variable of the widget object
langs = self.langs; // Calling the current object
langs.settings.apiurl = 'apiurl_new' // change the name of the field
self.set_lang(langs); // Change the current object to an object with a changed field
console.log(self.langs); // Output to the console to verify that the name has changed
Function set_settings ()
The set_settings () function allows you to add properties to the widget.
self.set_settings({par1:"text"}); //Setting is created with the name par1 and value text
self.get_settings();// In response you will get an array with an already created property
The function list_selected ()
The function list_selected () returns the contacts / leads highlighted by the tick from the contact / lead table as an array of objects: count_selected and selected. One of the selected objects contains an array of ticked objects with the properties emails, id, phones, type.
console.log(self.list_selected().selected); // Returns two objects, choose the object selected
// Result:
/*0: Object
emails: Array[1]
id: #id#
phones: Array[1]
type: "contact" */
WidgetsOverlay () function
The widgetsOverlay () (true / false) function enables or disables the overlay that appears when the widget is called from the contact list or leads.
// Example:
self.widgetsOverlay(true);
The function add_action ()
When the user works in the contacts and companies list area, you can provide a call to a function by clicking on the phone number or e-mail address of the contact.
The functions add_action () are passed parameters (type, action), where type is “e-mail” or “phone”, action is a function that will be called when you click on the phone number or e-mail address.
self.add_action("phone",function(){
/*
* code of interaction with the telephony widget
*/
});
The function add_source ()
Allows you to specify a new source that will be displayed in the control at the bottom of the lead card feed, buyer, contact or company.
At the moment, you can specify only one type of source – SMS
The parameters add_source () are passed parameters (source_type, handler), where source_type is “sms”, handler is a function that will be called when you click the “send” button.
The “handler” function must always return a Promise object
// example
self.add_source("sms", function(params) {
/*
params - this is the object in which there will be the necessary parameters for sending SMS
{
"phone": 75555555555, // recipient's phone number
"message": "sms text", // message to send
"contact_id": 12345 // contact ID to which the phone number is attached
}
*/
return new Promise(function (resolve, reject) {
// Here will describe the logic for sending SMS
$.ajax({
url: '/widgets/' + self.system().subdomain + '/loader/' + self.get_settings().widget_code +'/send_sms',
method: 'POST',
data: params,
success: function () {
// if successful, a note like 'sms' will be created
resolve();
},
error: function () {
reject();
}
});
});
});
The function get_pipeline_id ()
This function allows you to find out which pipeline the widget is connected to as a source. Available if there is a lead_sources area in manifest.json
self.get_pipeline_id()
.then(function (pipeline_id) {
// From here you can initiate a request using the pipeline ID
})
.catch(function () {
// handling of the case when the widget is not attached to the pipeline
});
Function set_status ()
A widget can have one of three statuses. The status of the widget is displayed in the settings area, on the widget’s icon. In the event that the widget uses the data entered by the user for the third-party service API, and this data is entered incorrectly, then this function can be used to display the error status.
Statuses are available for install (widget not active) and installed (widget is active), error (widget in error state).
// Example:
self.set_status('error');
The method crm_post (url, data, callback, type, error)
The method is used to send a request to your remote server through the Kommo proxy server. Its use is necessary, because when working with Kommo, the user works on a secure SSL protocol and the browser can block cross-domain requests. The best solution is to have a signed SSL certificate on the side of the internal system and work on HTTPS. The function is similar to jQuery post (), but the possibility of catching errors is added (the fifth argument is error), see the documentation (http://docs.jquery.com/Post)
Description of the method
Parameter | Type | Description |
---|---|---|
url | String | Link to the script processing data |
data optional |
Javascript object | Pairs “key-value” that will be sent to the server |
callback optional |
Function | A function that is called after each successful execution (in the case of type = text or html, it is always executed). |
type optional |
String | The type of data that the function returns: “xml”, “html”, “script”, “json”, “jsonp”, or “text”. |
error optional |
Function | A function that is called after each not successful execution (does not extend to type = text or html). |
Example of request
self.crm_post (
'http://www.test.com/file.php',
{
// We pass the POST data using the Javascript object model
name: 'myname',
login:'mylogin',
password: 'mypassword'
},
function(msg)
{
alert('It's all OK');
},
'text',
function()
{
alert ('Error');
}
)
Method self.get_settings
This method is necessary, in order to obtain the data that the user entered when the widget is connected. Data is returned as a javascript object
Example of the answer
{
login: "ZABRTEST",
password: "test",
maybe: "Y"
}
The method self.system ()
This method is necessary in order to obtain system data. Data is returned as a javascript object
Parameter | Description |
---|---|
area | The area on which the widget is currently playing |
kommouser_id | User ID |
kommouser | Mail of a user |
kommohash | The key for API authorization |
Example of the answer
{
area: "ccard",
kommouser_id: "103586",
kommouser: "testuser@kommo.com",
kommohash: "d053abd66063225fa8b763afz6496da8"
}
The method self.i18n (objname)
This method allows you to get an object from the language files in which there will be messages on the language locales used by the user
In objname, the name of the object to be extracted is passed
For example, call the function self.i18n (‘userLang’)
Example of the answer
{
firstWidgetText: "Click the button to send the data to a third-party server:",
textIntoTheButton: "Send data",
responseMessage: "Server Response :",
responseError: "Error"
}
Thus, having a simple tool for interacting with DOM and performing cross-domain queries, you can, besides creating simple text widgets, change the design of page elements, create your own information blocks based on external data, or vice versa, send data to external services, all of this works immediately for all users of your account.