<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Методы Qlik Sense Extension API - Qlik Sense - Обучение, учебник, онлайн курс</title>
	<atom:link href="https://qliksense.ivan-shamaev.ru/tag/%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D1%8B-qlik-sense-extension-api/feed/" rel="self" type="application/rss+xml" />
	<link>https://qliksense.ivan-shamaev.ru/tag/методы-qlik-sense-extension-api/</link>
	<description>Qlik Sense на русском языке. Пошаговые уроки для изучения Клик Сенс</description>
	<lastBuildDate>Sat, 12 Feb 2022 07:52:01 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>

<image>
	<url>https://qliksense.ivan-shamaev.ru/wp-content/uploads/2018/07/QlikSense_ICON2-150x150.png</url>
	<title>Методы Qlik Sense Extension API - Qlik Sense - Обучение, учебник, онлайн курс</title>
	<link>https://qliksense.ivan-shamaev.ru/tag/методы-qlik-sense-extension-api/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Qlik Web Developer &#8211; Backend API для разработки Extensions</title>
		<link>https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/</link>
					<comments>https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Sat, 16 Jan 2021 12:42:03 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Backend API]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[extension api]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<category><![CDATA[Qlik Web Developer]]></category>
		<category><![CDATA[Методы Qlik Sense Extension API]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2530</guid>

					<description><![CDATA[<p>Введение в Qlik Sense Backend API Backend API состоит из ряда методов и используется для взаимодействия с ассоциативным механизмом Qlik. Он предоставляет вспомогательные функции для вызовов ассоциативного механизма Qlik и доступ к данным ассоциативного механизма Qlik. Backend API &#8211; это оболочка для выбранных методов Qlik Engine JSON API, но с той разницей, что Backend API знает контекст, то есть<a class="moretag" href="https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/">Qlik Web Developer &#8211; Backend API для разработки Extensions</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Введение в Qlik Sense Backend API</h1>
<p><span><strong>Backend API</strong> состоит из ряда методов и используется для взаимодействия с </span><span class="CommonComponentsEngineName"><span>ассоциативным механизмом Qlik</span></span><span>. Он предоставляет вспомогательные функции для вызовов </span><span class="CommonComponentsEngineName"><span>ассоциативного механизма Qlik</span></span><span> и доступ к данным </span><span class="CommonComponentsEngineName"><span>ассоциативного механизма Qlik</span></span><span>. </span></p>
<p><span><strong>Backend API</strong> &#8211; это оболочка для выбранных методов <span class="CommonComponentsProtocolName"><strong>Qlik Engine JSON API</strong>,</span> но с той разницей, что Backend API знает контекст, то есть текущее соединение <strong>WebSocket</strong> и приложение <span class="CommonComponentsQlik Sense">Qlik Sense</span>.</span></p>
<p><span>Backend API доступен для разработчиков расширений как </span><code><span class="syntax"><span>this.backendApi</span></span></code><span>:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.eachDataRow(function(rownum, row) {
    .....
});</pre>
<h2>Примеры использования</h2>
<h3>Selections</h3>
<p><span>Следующие методы <strong>Backend API</strong> доступны, когда вы работаете с выборками в универсальном объекте, стоящем за вашим расширением.</span></p>
<ul>
<li><code><span class="syntax">selectValues</span></code></li>
<li><code><span class="syntax">selectRange</span></code></li>
<li><code><span class="syntax">clearSelections</span></code></li>
<li><code><span class="syntax">hasSelections</span></code></li>
</ul>
<p><span>Используйте метод </span><code><span class="syntax"><span>selectValues</span></span></code><span> для выбора значений в этом объекте. Вызов вызывает перерисовку объекта.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">$element.find('li').on('click', function() {
    if(this.hasAttribute("data-value")) {
        var value = parseInt(this.getAttribute("data-value"), 10), dim = 0;
        self.backendApi.selectValues(dim, [value], true);
    }
});</pre>
<p><span>Метод </span><code><span class="syntax"><span>selectRange</span></span></code><span> выбирает значения в этом объекте, используя диапазоны.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var range = {
    "qMeasureIx": 1,
    "qRange": {
        "qMin": 10,
        "qMax": 100,
        "qMinInclEq": true,
        "qMaxInclEq": true
    }
};
self.backendApi.selectRange( [range], false);</pre>
<p><span>Метод </span><code><span class="syntax"><span>hasSelections</span></span></code><span> можно использовать, чтобы узнать, есть ли неподтвержденные выборы для этого объекта.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.hasSelections();</pre>
<p><span>Если для этого объекта есть неподтвержденные выборки, вы можете использовать метод </span><span class="syntax"><span><code>clearSelections</code>,</span></span><span> чтобы очистить их.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.clearSelections();</pre>
<h3>Search</h3>
<p><span>При работе с поиском в объектах списка вы можете использовать следующие методы Backend API.</span></p>
<ul>
<li><code><span class="syntax">search</span></code></li>
<li><code><span class="syntax">acceptSearch</span></code></li>
<li><code><span class="syntax">abortSearch</span></code></li>
</ul>
<p><span>Используйте </span><span>метод </span><span class="syntax"><code>search</code> </span><span>для поиска термина в <strong>list object</strong>. Это приводит к обновленному <strong>layout</strong>, который содержит только совпадающие записи.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.search("A");</pre>
<p><span>Метод </span><code><span class="syntax"><span>acceptSearch</span></span></code><span> принимает результат поиска и делает его выбранным в поле.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.acceptSearch(false);</pre>
<p><span>Используйте метод </span><span class="syntax"><span><code>abortSearch</code>,</span></span><span> если поиск должен быть прерван. Это очищает существующий поиск и возвращает объект в состояние, в котором он находился до поиска.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">this.backendApi.abortSearch();</pre>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/">Qlik Web Developer &#8211; Backend API для разработки Extensions</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/qlik-extensions-web-development-backend-api/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Обзор Extension API и описание методов расширений</title>
		<link>https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/</link>
					<comments>https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Sat, 09 Jan 2021 20:42:37 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[extension api]]></category>
		<category><![CDATA[Methods Qlik Sense Extension API]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<category><![CDATA[Методы Qlik Sense Extension API]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2494</guid>

					<description><![CDATA[<p>Введение в Extension API Extension API состоит из методов и свойств, используемых для создания custom расширений визуализации (или custom visualization extensions). Обзор Visualization extension templates Для начала рассмотрим шаблоны Visualization extension templates. В dev-hub при создании Extension вам предлагают выбрать шаблон. Basic Visualization template Назовем наш Extension &#8220;1_BVT&#8221;. При инициализации<a class="moretag" href="https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/">Обзор Extension API и описание методов расширений</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Введение в Extension API</h1>
<p><span><strong>Extension API</strong> состоит из методов и свойств, используемых для создания custom расширений визуализации (или <strong>custom visualization extensions</strong>).</span></p>
<h2>Обзор Visualization extension templates</h2>
<p>Для начала рассмотрим шаблоны <strong>Visualization extension templates</strong>. В <strong>dev-hub</strong> при создании Extension вам предлагают выбрать шаблон.</p>
<h3>Basic Visualization template</h3>
<p>Назовем наш Extension <strong>&#8220;1_BVT&#8221;</strong>. При инициализации создадутся два файла:</p>
<p><strong>1_BVT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "1_BVT",
    "description": "Basic empty visualization template",
    "type": "visualization",
    "version": "1.0.0",
    "icon": "extension",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>1_BVT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [ "qlik"
],
function ( qlik) {

    return {
        support : {
            snapshot: true,
            export: true,
            exportData : false
        },
        paint: function ($element) {
            //add your rendering code here
            $element.html( "1_BVT" );
            //needed for export
            return qlik.Promise.resolve();
        }
    };

} );</pre>
<h3>Chart Template</h3>
<p>Назовем наш Extension <strong>&#8220;2_CT&#8221;</strong>. При инициализации создадутся 4 файла:</p>
<p><strong>2_CT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "2_CT",
    "description": "Chart visualization template",
    "icon": "bar-chart-horizontal",
    "type": "visualization",
    "version": "1.0.0",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>2_CT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( ["qlik", "text!./2_CT.ng.html", "css!./2_CT.css"],
    function ( qlik, template ) {
        "use strict";
        return {
            template: template,
            initialProperties: {
                qHyperCubeDef: {
                    qDimensions: [],
                    qMeasures: [],
                    qInitialDataFetch: [{
                        qWidth: 2,
                        qHeight: 50
                    }]
                }
            },
            definition: {
                type: "items",
                component: "accordion",
                items: {
                    dimensions: {
                        uses: "dimensions",
                        min: 1,
                        max: 1
                    },
                    measures: {
                        uses: "measures",
                        min: 1,
                        max: 1
                    },
                    sorting: {
                        uses: "sorting"
                    }
                }
            },
            support: {
                snapshot: true,
                export: true,
                exportData: true
            },
            paint: function () {
                //needed for export
                this.$scope.selections = [];
                return qlik.Promise.resolve();
            },
            controller: ["$scope", "$element", function ( $scope ) {
                $scope.getPercent = function ( val ) {
                    return Math.round( (val * 100 / $scope.layout.qHyperCube.qMeasureInfo[0].qMax) * 100 ) / 100;
                };

                $scope.selections = [];

                $scope.sel = function ( $event ) {
                    if ( $event.currentTarget.hasAttribute( "data-row" ) ) {
                        var row = parseInt( $event.currentTarget.getAttribute( "data-row" ), 10 ), dim = 0,
                            cell = $scope.$parent.layout.qHyperCube.qDataPages[0].qMatrix[row][0];
                        if ( cell.qIsNull !== true ) {
                            cell.qState = (cell.qState === "S" ? "O" : "S");
                            if ( $scope.selections.indexOf( cell.qElemNumber ) === -1 ) {
                                $scope.selections.push( cell.qElemNumber );
                            } else {
                                $scope.selections.splice( $scope.selections.indexOf( cell.qElemNumber ), 1 );
                            }
                            $scope.selectValues( dim, [cell.qElemNumber], true );
                        }
                    }
                };
            }]
        };

    } );</pre>
<p><strong>2_CT.css</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">.qv-object-2_CT .css_bars_row {
    transition: all 0.3s ease;
    float: left;
    width: 98%;
    overflow: hidden;
    border: 1px solid #ccc;
    background-color: #f0f0f0;
    margin-bottom: 2px;
    border-radius: 0 10px 10px 0;
    -moz-border-radius: 0 10px 10px 0;
    cursor: pointer;
}

.qv-object-2_CT .css_bars_bar {
    float: left;
    clear: left;
    height: 30px;
    background-color: #5f5f5f;
}

.qv-object-2_CT .css_bars_bar span {
    font-weight: bold;
    float: left;
    margin-left: 5px;
    margin-right: 5px;
    margin-top: 7px;
    color: #f0f0f0;
    overflow: hidden;
    text-overflow: ellipsis;
    width: 90%;
    white-space: nowrap;
}

.qv-object-2_CT .css_bars_per {
    position: relative;
}

.qv-object-2_CT .css_bars_per span {
    font-weight: bold;
    float: left;
    margin-right: 5px;
    margin-top: 5px;
    margin-left: 5px;
}

.qv-object-2_CT .css_bars_per span.over {
    position: absolute;
    right: 15px;
    color: #f0f0f0;
}

.qv-object-2_CT .css_bars_row:hover, .qv-object-2_CT .css_bars_row.selected {
    border: 1px solid #000;
}</pre>
<p><strong>2_CT.ng.html</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;div qv-extension style="height: 100%; position: relative; overflow: auto;"&gt;
    &lt;div class="css_bars_row" ng-repeat="item in layout.qHyperCube.qDataPages[0].qMatrix" title="{{item[0].qText}}" data-row="{{ $index }}"
         ng-click="sel($event)" data-value="{{ item[0].qElemNumber }}" ng-class="{selected: (item[0].qState) === 'S' || selections.indexOf(item[0].qElemNumber) !== -1 }"&gt;
        &lt;div class="css_bars_bar" ng-style="{'width':getPercent(item[1].qNum)+'%'}"&gt;&lt;span&gt;{{item[0].qText}}&lt;/span&gt;&lt;/div&gt;
        &lt;div class="css_bars_per"&gt;
            &lt;span ng-class="{over: (getPercent(item[1].qNum)&gt;95)}"&gt;{{getPercent(item[1].qNum)}} %&lt;/span&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<h3>Listbox template</h3>
<p>Назовем наш Extension &#8220;3_LBT&#8221;. При инициализации создадутся 3 файла:</p>
<p><strong>3_LBT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( ["qlik", "jquery", "text!./style.css"], function ( qlik, $, cssContent ) {
    'use strict';
    $( "&lt;style&gt;" ).html( cssContent ).appendTo( "head" );
    return {
        initialProperties: {
            qListObjectDef: {
                qShowAlternatives: true,
                qFrequencyMode: "V",
                qInitialDataFetch: [{
                    qWidth: 2,
                    qHeight: 50
                }]
            }
        },
        definition: {
            type: "items",
            component: "accordion",
            items: {
                dimension: {
                    type: "items",
                    label: "Dimensions",
                    ref: "qListObjectDef",
                    min: 1,
                    max: 1,
                    items: {
                        label: {
                            type: "string",
                            ref: "qListObjectDef.qDef.qFieldLabels.0",
                            label: "Label",
                            show: true
                        },
                        libraryId: {
                            type: "string",
                            component: "library-item",
                            libraryItemType: "dimension",
                            ref: "qListObjectDef.qLibraryId",
                            label: "Dimension",
                            show: function ( data ) {
                                return data.qListObjectDef &amp;&amp; data.qListObjectDef.qLibraryId;
                            }
                        },
                        field: {
                            type: "string",
                            expression: "always",
                            expressionType: "dimension",
                            ref: "qListObjectDef.qDef.qFieldDefs.0",
                            label: "Field",
                            defaultValue: "=ValueList('A','B','C')",
                            show: function ( data ) {
                                return data.qListObjectDef &amp;&amp; !data.qListObjectDef.qLibraryId;
                            }
                        },
                        frequency: {
                            type: "string",
                            component: "dropdown",
                            label: "Frequency mode",
                            ref: "qListObjectDef.qFrequencyMode",
                            options: [{
                                value: "N",
                                label: "No frequency"
                            }, {
                                value: "V",
                                label: "Absolute value"
                            }, {
                                value: "P",
                                label: "Percent"
                            }, {
                                value: "R",
                                label: "Relative"
                            }],
                            defaultValue: "V"
                        }
                    }
                },
                settings: {
                    uses: "settings"
                }
            }
        },
        support : {
            snapshot: true,
            export: true,
            exportData : false
        },
        paint: function ( $element,layout ) {
            var self = this, html = "&lt;ul&gt;";
            layout.qListObject.qDataPages[0].qMatrix.forEach( function ( row ) {
                html += '&lt;li class="data state' + row[0].qState + '" data-value="' + row[0].qElemNumber + '"&gt;' + row[0].qText;
                if ( row[0].qFrequency ) {
                    html += '&lt;span&gt;' + row[0].qFrequency + '&lt;/span&gt;';
                }
                html += '&lt;/li&gt;';
            } );
            html += "&lt;/ul&gt;";
            $element.html( html );
            if ( this.selectionsEnabled ) {
                $element.find( 'li' ).on( 'click', function () {
                    if ( this.hasAttribute( "data-value" ) ) {
                        var value = parseInt( this.getAttribute( "data-value" ), 10 ), dim = 0;
                        self.selectValues( dim, [value], true );
                        this.classList.toggle("selected");
                    }
                } );
            }
            return qlik.Promise.resolve();
        }
    };
} );
</pre>
<p><strong>3_LBT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "3_LBT",
    "description": "Listbox visualization template",
    "icon": "list",
    "type": "visualization",
    "version": "1.0.0",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>style.css</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">.qv-object-3_LBT div.qv-object-content-container {
    overflow: auto;
}
.qv-object-3_LBT ul {
    list-style: none;
}
.qv-object-3_LBT li.data {
    padding: 4px;
    border-bottom: 1px solid #ddd;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* frequency */
.qv-object-3_LBT li.data span
{
    padding-left: 10px;
    font-style: italic;
    float:right;
}
/* colors when NOT in selection mode */

.qv-object-3_LBT li.stateS, .qv-object-3_LBT li.stateL {
    background-image: linear-gradient(top, #52cc52, #4dc04d);
    background-image: -o-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -moz-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -webkit-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -ms-linear-gradient(top, #52cc52, #4dc04d);
    color: #fff;
}

.qv-object-3_LBT li.stateX, .qv-object-3_LBT li.stateXL, .qv-object-3_LBT li.stateXS {
    background-color: #A9A9A9;
    color: #fff;
}

.qv-object-3_LBT li.stateA {
    background-image: none;
    background-color: #ddd;
    color: #000;
}
/* colors when in selection mode */
.qv-object-3_LBT.qv-selections-active li {
    background-image: none;
    background-color: #ddd;
    color: #000;
}

.qv-object-3_LBT.qv-selections-active li.selected {
    background-image: linear-gradient(top, #52cc52, #4dc04d);
    background-image: -o-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -moz-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -webkit-linear-gradient(top, #52cc52, #4dc04d);
    background-image: -ms-linear-gradient(top, #52cc52, #4dc04d);
    color: #fff;
}</pre>
<h3>Angular Basic Visualization template</h3>
<p>Назовем наш Extension <strong>&#8220;4_ABVT&#8221;</strong>. При инициализации создадутся 3 файла:</p>
<p><strong>4_ABVT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "4_ABVT",
    "description": "Basic empty visualization template based on angular.js",
    "type": "visualization",
    "version": "1.0.0",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>4_ABVT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( ["qlik", "text!./template.html"],
    function ( qlik, template ) {

        return {
            template: template,
            support: {
                snapshot: true,
                export: true,
                exportData: false
            },
            paint: function () {
                return qlik.Promise.resolve();
            },
            controller: ['$scope', function ( $scope ) {
                //add your rendering code here
                $scope.html = "Hello World";
            }]
        };

    } );</pre>
<p><strong>template.html</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;div qv-extension style="height: 100%; position: relative; overflow: auto;" class="ng-scope"&gt;
    {{ html }} 
&lt;/div&gt;</pre>
<h3>Angular Table template</h3>
<p>Назовем наш Extension <strong>&#8220;5_ATT&#8221;</strong>. При инициализации создадутся 4 файла:</p>
<p><strong>5_ATT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( ["qlik","jquery", "text!./style.css", "text!./template.html"], function (qlik, $, cssContent, template ) {'use strict';
    $("&lt;style&gt;").html(cssContent).appendTo("head");
    return {
       template: template,
       initialProperties : {
            qHyperCubeDef : {
                qDimensions : [],
                qMeasures : [],
                qInitialDataFetch : [{
                    qWidth : 10,
                    qHeight : 50
                }]
            }
        },
        definition : {
            type : "items",
            component : "accordion",
            items : {
                dimensions : {
                    uses : "dimensions",
                    min : 1
                },
                measures : {
                    uses : "measures",
                    min : 0
                },
                sorting : {
                    uses : "sorting"
                },
                settings : {
                    uses : "settings",
                    items : {
                        initFetchRows : {
                            ref : "qHyperCubeDef.qInitialDataFetch.0.qHeight",
                            label : "Initial fetch rows",
                            type : "number",
                            defaultValue : 50
                        }
                    }
                }
            }
        },
        support : {
            snapshot: true,
            export: true,
            exportData : true
        },
        paint: function ( ) {
            //setup scope.table
            if ( !this.$scope.table ) {
                this.$scope.table = qlik.table( this );
            }
            return qlik.Promise.resolve();
        },
        controller: ['$scope', function (/*$scope*/) {
        }]
    };

} );
</pre>
<p><strong>5_ATT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "5_ATT",
    "description": "Table visualization template based on angular.js",
    "icon": "table",
    "type": "visualization",
    "version": "1.0.0",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>style.css</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">.qv-object-5_ATT div.qv-object-content-container {
    overflow: auto;
}
.qv-object-5_ATT td,
.qv-object-5_ATT th {
    border-top: 0px solid #fff;
    border-bottom: 1px solid #ddd;
    border-right: 1px solid #ddd;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
    cursor: default;
    font-size: 12px;
}
.qv-object-5_ATT td.numeric {
    text-align: right;
}
.qv-object-5_ATT button {
    width: 100%;
}</pre>
<p><strong>template.html</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;div qv-extension style="height: 100%; position: relative; overflow: auto;"&gt;
    &lt;table&gt;
        &lt;thead&gt;
        &lt;tr&gt;
            &lt;th ng-repeat="head in table.headers track by $index" ng-click="head.orderBy()"&gt;{{head.qFallbackTitle}}&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        &lt;tr ng-repeat="row in table.rows track by $index"&gt;
            &lt;td ng-repeat="cell in row.cells track by $index" class="selectable" ng-class="{'selected':cell.selected,'numeric':cell.qNum}" ng-click="cell.select($event)"&gt;{{cell.qText}}&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
    &lt;button ng-if="table.rowCount&gt;table.rows.length" ng-click="table.getMoreData()" class="lui-button more"&gt;More...&lt;/button&gt;
&lt;/div&gt;
</pre>
<h3>Table template</h3>
<p>Назовем наш Extension <strong>&#8220;6_TT&#8221;</strong>. При инициализации создадутся 3 файла:</p>
<p><strong>6_TT.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">/*globals define*/
define( ["qlik", "jquery", "text!./style.css"], function ( qlik, $, cssContent ) {
    'use strict';
    $( "&lt;style&gt;" ).html( cssContent ).appendTo( "head" );
    function createRows ( rows, dimensionInfo ) {
        var html = "";
        rows.forEach( function ( row ) {
            html += '&lt;tr&gt;';
            row.forEach( function ( cell, key ) {
                if ( cell.qIsOtherCell ) {
                    cell.qText = dimensionInfo[key].othersLabel;
                }
                html += "&lt;td ";
                if ( !isNaN( cell.qNum ) ) {
                    html += "class='numeric'";
                }
                html += '&gt;' + cell.qText + '&lt;/td&gt;';
            } );
            html += '&lt;/tr&gt;';
        } );
        return html;
    }

    return {
        initialProperties: {
            qHyperCubeDef: {
                qDimensions: [],
                qMeasures: [],
                qInitialDataFetch: [{
                    qWidth: 10,
                    qHeight: 50
                }]
            }
        },
        definition: {
            type: "items",
            component: "accordion",
            items: {
                dimensions: {
                    uses: "dimensions",
                    min: 1
                },
                measures: {
                    uses: "measures",
                    min: 0
                },
                sorting: {
                    uses: "sorting"
                },
                settings: {
                    uses: "settings"
                }
            }
        },
        snapshot: {
            canTakeSnapshot: true
        },
        paint: function ( $element, layout ) {
            var html = "&lt;table&gt;&lt;thead&gt;&lt;tr&gt;", self = this,
                morebutton = false,
                hypercube = layout.qHyperCube,
                rowcount = hypercube.qDataPages[0].qMatrix.length,
                colcount = hypercube.qDimensionInfo.length + hypercube.qMeasureInfo.length;
            //render titles
            hypercube.qDimensionInfo.forEach( function ( cell ) {
                html += '&lt;th&gt;' + cell.qFallbackTitle + '&lt;/th&gt;';
            } );
            hypercube.qMeasureInfo.forEach( function ( cell ) {
                html += '&lt;th&gt;' + cell.qFallbackTitle + '&lt;/th&gt;';
            } );
            html += "&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;";
            //render data
            html += createRows( hypercube.qDataPages[0].qMatrix, hypercube.qDimensionInfo );
            html += "&lt;/tbody&gt;&lt;/table&gt;";
            //add 'more...' button
            if ( hypercube.qSize.qcy &gt; rowcount ) {
                html += "&lt;button class='more'&gt;More...&lt;/button&gt;";
                morebutton = true;
            }
            $element.html( html );
            if ( morebutton ) {
                $element.find( ".more" ).on( "click", function () {
                    var requestPage = [{
                        qTop: rowcount,
                        qLeft: 0,
                        qWidth: colcount,
                        qHeight: Math.min( 50, hypercube.qSize.qcy - rowcount )
                    }];
                    self.backendApi.getData( requestPage ).then( function ( dataPages ) {
                        rowcount += dataPages[0].qMatrix.length;
                        if ( rowcount &gt;= hypercube.qSize.qcy ) {
                            $element.find( ".more" ).hide();
                        }
                        var html = createRows( dataPages[0].qMatrix, hypercube.qDimensionInfo );
                        $element.find( "tbody" ).append( html );
                    } );
                } );
            }
            return qlik.Promise.resolve();
        }
    };
} );
</pre>
<p><strong>6_TT.qext</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "name": "6_TT",
    "description": "Table visualization template",
    "icon": "table",
    "type": "visualization",
    "version": "1.0.0",
    "author": "",
    "homepage": "",
    "keywords": "qlik-sense, visualization",
    "license": "",
    "repository": "",
    "dependencies": {
        "qlik-sense": "&gt;=3.0.x"
    }
}</pre>
<p><strong>style.css</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">.qv-object-6_TT div.qv-object-content-container {
    overflow: auto;
}
.qv-object-6_TT td,
.qv-object-6_TT th {
    border-top: 0px solid #fff;
    border-bottom: 1px solid #ddd;
    border-right: 1px solid #ddd;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
    cursor: default;
    font-size: 12px;
}
.qv-object-6_TT td.numeric {
    text-align: right;
}
.qv-object-6_TT button {
    width: 100%;
}</pre>
<h2>Структура расширения</h2>
<p>Как мы видим из примеров стандартных шаблонов экстеншенов для создания расширения необходимо минимум 2 файла &#8211; js и qext. Для более богатой функциональности добавляются css и html.</p>
<p><strong>Опишем зачем нужны файлы:</strong></p>
<ul>
<li><span style="color: #ff6600;"><em><strong>&lt;name_extension&gt;</strong></em></span>.<strong>js</strong> &#8211; это код Javascript &#8211; фактически это ядро вашего Extension. Здесь происходит отрисовка визуализации (например, в цикле заполняется html код с тегами при обходе гиперкуба qlik sense), настраивается логика работы с выборкой и другие действия.</li>
<li><span style="color: #ff6600;"><em><strong>&lt;name_extension&gt;</strong></em></span>.<strong>qext</strong> &#8211; <span><span style="color: #339966;"><strong>extension metadata file (QEXT)</strong></span> &#8211; файл JSON, который используется <span class="CommonComponentsQlik Sense">Qlik Sense</span> для идентификации расширения визуализации. В этом файле указывается название расширения, тип расширения, версия, автор, ключевые слова. И другие атрибуты, которые будут расмотрены в дальнейших разделах.</span></li>
<li><span style="color: #ff6600;"><em><strong>&lt;name_extension&gt;</strong></em></span>.<strong>css</strong> &#8211; в этом файле прописываются стили для кастомной визуализации (т.е. вашего экстеншена).</li>
<li><span style="color: #ff6600;"><em><strong>&lt;name_extension&gt;</strong></em></span>.<strong>html</strong> &#8211; шаблон html кода, который будет использоваться в js коде и заполняться внутри js кода (например в цикле). Этот прием используется, например, в Angular Framework.</li>
<li><strong>wbfolder.wbl<sup>*</sup></strong> (<span>не является обязательным</span>) &#8211; <span>Файл загрузки Workbench &#8211; используется редактором Extension &amp; Mashup и содержит список файлов, которые должны быть загружены в редактор dev-hub (без него вы не сможете открыть или продублировать расширение в dev-hub).</span><br />
<span>Когда вы сохраняете проект расширения или Mashup из редактора, WBL-файл автоматически создается и включается в проект. Если вы сделаете копию расширения или мэшапа, то будут скопированы только файлы, перечисленные в wbfolder.wbl.<br />
Добавляя дополнительные имена файлов &#8211; разделенные точкой с запятой и переносом строки &#8211; в wbfolder.wbl, вы также можете разрешить загрузку этих дополнительных файлов. При отсутствии файла может возникнуть ошибка <span style="color: #800080;"><em><strong>Incomplete visualization. The visualization is incomplete and cannot be opened or duplicated (wbfolder.wbl file missing).</strong></em></span></span></li>
</ul>
<p><strong>Рассмотрим процесс отрисовки экстеншена:</strong></p>
<p><span>Когда пользователь открывает лист, </span><span class="PrimaryGenericName"><span>Qlik Sense</span></span><span> извлекает визуализации на этом листе, а затем выполняет стандартный рабочий процесс для каждой визуализации.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/Qlik_Sense_Extension_API_concepts_Overview.png"><img fetchpriority="high" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/Qlik_Sense_Extension_API_concepts_Overview.png" alt="" width="536" height="698" class="aligncenter size-full wp-image-2504" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/Qlik_Sense_Extension_API_concepts_Overview.png 536w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/Qlik_Sense_Extension_API_concepts_Overview-230x300.png 230w" sizes="(max-width: 536px) 100vw, 536px" /></a></p>
<p>Пример <strong>Hello World</strong> можно посмотреть на сайте <strong><a href="http://qliksite.io/tutorials/qliksense-visualization-extensions/part-01/03-Lets-Get-Started--Hello-World-Example/" target="_blank" rel="noopener">http://qliksite.io/tutorials/qliksense-visualization-extensions/part-01/03-Lets-Get-Started&#8211;Hello-World-Example/</a></strong></p>
<h2>Схема работы JS кода в QS Extension</h2>
<p><code>define</code> <span>&#8211; это концепция, введенная </span><strong><a href="https://requirejs.org/" target="_blank" rel="noopener">RequireJS</a></strong><span><strong> </strong>для определения зависимостей в ваших файлах JavaScript. Идея состоит в том, чтобы загрузить внешние зависимости до того, как будет выполнен ваш основной скрипт. </span></p>
<p><span><strong>Зависимости</strong> – внешние модули (скрипты/библиотеки), которые необходимы для работы нашего расширения.</span></p>
<p><code>paint</code><span> &#8211; основной метод визуализации визуализации, который будет вызываться каждый раз, когда визуализация должна быть отрисована, либо из-за новых данных с сервера, либо из-за изменения размера.</span></p>
<p><span>Метод paint получает два параметра </span><code>$element</code><span>и </span><code>layout</code><span>.</span></p>
<table>
<thead>
<tr>
<th><strong>Параметр</strong></th>
<th><strong>Описание</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>$element</code></td>
<td><span>Оболочка jQuery, содержащая родительский элемент HTML, в котором должна отображаться визуализация.</span></td>
</tr>
<tr>
<td><code>layout</code></td>
<td><span>Данные и свойства для визуализации.</span></td>
</tr>
</tbody>
</table>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qliksense_requirejs_define.png"><img decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qliksense_requirejs_define.png" alt="" width="674" height="882" class="aligncenter size-full wp-image-2507" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qliksense_requirejs_define.png 674w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qliksense_requirejs_define-229x300.png 229w" sizes="(max-width: 674px) 100vw, 674px" /></a></p>
<p>todo</p>
<h1>Видео &#8220;How to build Qlik Sense Visualization Extensions &#8211; Introduction&#8221;</h1>
<p><iframe title="How to build Qlik® Sense Visualization Extensions - 1. Introduction" width="750" height="422" src="https://www.youtube.com/embed/n7RICsLGiCE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h1>Методы Qlik Sense Extension API</h1>
<h2>beforeDestroy</h2>
<p><span>Выполняется до того, как объект будет уничтожен путем его удаления, переключения листа, открытия инструмента выделения или любых других средств удаления расширения. </span><span>Используйте этот метод для того, чтобы удалить привязки и предотвратить утечку памяти.</span></p>
<blockquote><p><span><strong>Утечка памяти</strong> — это, в широком смысле, фрагмент памяти, выделенной приложению, который больше не нужен этому приложению, но не может быть возвращён операционной системе для дальнейшего использования. Другими словами это — блок памяти, который захвачен приложением без намерения пользоваться этой памятью в будущем.</span></p>
<p><strong>Источник:</strong> <a href="https://habr.com/ru/company/ruvds/blog/495898/" target="_blank" rel="noopener">Практическое руководство по борьбе с утечками памяти в Node.js</a></p></blockquote>
<h2>mounted</h2>
<p><span>Выполняется один раз при инициализации объекта.</span></p>
<p><code><span>mounted($element)</span></code></p>
<h2>paint</h2>
<p><span>Отрисовывает (или render) визуализацию расширения. </span><span>Вызовите этот метод, когда необходимо отрисовать visualization extension либо из-за новых данных с сервера, либо из-за изменения размера расширения визуализации.</span></p>
<h3>Параметры метода paint</h3>
<h4>$element</h4>
<p><span>Оболочка jQuery, содержащая HTML-элемент, в котором должна отображаться визуализация.</span></p>
<h4>layout</h4>
<p><span>Данные и свойства для визуализации.</span></p>
<h4>layout.qHyperCube.qDimensionInfo</h4>
<p><span>Массив с данными об используемых dimensions. Включает <code>qFallbackTitle</code></span><span>, заголовок и </span><code><span class="syntax"><span>qCardinal</span></span></code><span> , количество различных значений.</span></p>
<h4>layout.qHyperCube.qMeasureInfo</h4>
<p><span>Массив с данными об используемых мерах. Включает </span><code><span class="syntax"><span>qFallbackTitle</span></span></code><span>, заголовок (title), </span><code><span class="syntax"><span>qCardinal</span></span></code><span>, количество различных значений, </span><code><span class="syntax"><span>qMin</span></span></code><span> и </span><code><span class="syntax"><span>qMax</span></span></code><span> для минимального и максимального значений.</span></p>
<h4>layout.qHyperCube.qDataPages[0].qMatrix</h4>
<p><span>Массив с данными из </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span> . Каждому объекту соответствует строка в результате. Значения измерения (dimension) и меры (measure) являются объектами JavaScript в этом массиве.</span></p>
<h4>layout.qHyperCube.qDataPages[0].qMatrix[0..#dimensions – 1]</h4>
<p><span>Значения Dimension для строки результата. Каждый объект содержит текст ( </span><code><span class="syntax"><span>qText</span></span></code><span>), используемый для рендеринга, и значение ( </span><code><span class="syntax"><span>qElemNumber</span></span></code><span> ), используемое для selections, и состояние значения ( </span><code><span class="syntax"><span>qState</span></span></code><span> ).</span></p>
<h4>layout.qHyperCube.qDataPages[0].qMatrix[#dimensions..]</h4>
<p><span>Массив со значениями measure для строки результата. Каждый объект содержит данные, фактическое значение ( </span><code><span class="syntax"><span>qNum</span></span></code><span> ) и текст ( </span><code><span class="syntax"><span>qText</span></span></code><span> ), форматированное значение.</span></p>
<h4>layout.qHyperCube.qSize</h4>
<p><span>Общее количество строк (</span><code><span class="syntax"><span>qcy</span></span></code><span>) и столбцов (</span><code><span class="syntax"><span>qcx</span></span></code><span>) в результирующем наборе.</span></p>
<h4>layout.qInfo.qId</h4>
<p><span><code>qId</code> &#8211; Уникальный идентификатор объекта. </span><span>При необходимости полезно для создания уникального HTML-идентификатора.</span></p>
<h4>layout.qSelectionInfo</h4>
<p><span>Если пользователь находится в режиме выбора, это будет объект с двумя флагами: </span><code><span class="syntax"><span>qInSelections</span></span></code><span> и </span><code><span class="syntax"><span>qMadeSelections</span></span></code><span>.</span></p>
<h2>updateData</h2>
<p><span>Выполняется каждый раз при изменении layout или данных, например при осуществлении выборок или изменении свойств. </span><span>Получает новый layout и возвращает promise.</span></p>
<p><code><span>updateData(layout)</span></code></p>
<p>&nbsp;</p>
<h1>Properties Qlik Sense Extension API</h1>
<h2>initialProperties</h2>
<p><code><span>initialProperties()</span></code></p>
<p><span>Задает свойства, которые объект должен иметь при создании. Определите либо как гиперкуб ( </span><code><span class="syntax"><span>qHyperCubeDef</span></span></code><span> ), либо как объект списка ( </span><code><span class="syntax"><span>qListObjectDef</span></span></code><span> ).</span></p>
<p><span>Настройте все свойства и назначьте значения по умолчанию для <strong>custom properties</strong>.</span></p>
<p><span>Максимальное количество ячеек ( </span><code><span class="statement"><span>qWidth</span></span></code><span> * </span><code><span class="statement"><span>qHeight</span></span></code><span> ), разрешенное при исходной <span class="statement">выборке</span> данных, составляет 10 000. Получите дополнительные данные по частям с помощью метода </span><code><span class="syntax"><span>getData</span></span></code><span> в <strong>Backend API</strong>.</span></p>
<p><strong><span class="autonumber">Пример:</span> использование гиперкуба</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">initialProperties : {
    qHyperCubeDef : {
        qDimensions : [],
        qMeasures : [],
        qInitialDataFetch : [{
            qWidth : 2,
            qHeight : 50
        }]
    }
}</pre>
<p><strong><span class="autonumber">Пример</span>, в котором используется объект списка, а также определяются некоторые custom properties</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">initialProperties : {
    qListObjectDef : {
        qShowAlternatives : true,
        qFrequencyMode : "V",
        qInitialDataFetch : [{
            qWidth : 2,
            qHeight : 50
        }]
    },
    fixed : true,
    width : 25,
    percent : true,
    selectionMode : "CONFIRM"
}</pre>
<h3>Ограничение 10000 ячеек</h3>
<p>Ходят упорные слухи, что при работе с HyperCube с использованием клиентских API (в расширениях визуализации или мэшапах) можно загрузить только 10 000 ячеек данных.</p>
<p><span style="color: #ff9900;"><strong>Этот слух не соответствует действительности !!!</strong></span></p>
<p>Вместо этого верно, что вы можете получить только 10 000 записей на страницу данных из Engine, но вы, безусловно, можете запросить получение дополнительных страниц данных.<br />
Таким образом, разработчик полностью решает, сколько точек данных из источника данных используется в пользовательском интерфейсе.</p>
<h2>snapshot</h2>
<p><span>Включает или отключает возможность делать снимки расширения визуализации для использования в Data Storytelling.</span></p>
<p><code><span>snapshot(enable)</span></code></p>
<h2>export</h2>
<p><span>Включает или отключает возможность экспорта расширения визуализации как изображения или как <strong>PDF-файл.</strong> Это также позволяет включить расширение визуализации в <strong>экспорт из Storytelling</strong> (PowerPoint или PDF).</span></p>
<p><span>Если задано значение </span><code><span class="syntax"><span>true</span></span></code><span> , включены параметры контекстного меню <strong>«</strong></span><strong><span class="ui_item">Экспорт как изображение»</span></strong><span> и <strong>«</strong></span><strong><span class="ui_item">Экспорт в PDF»</span></strong><span> для расширения визуализации. Это также позволяет расширениям визуализации быть частью историй, экспортируемых в PowerPoint или PDF, в параметрах контекстного меню «</span><span class="ui_item"><span>Экспорт истории в PowerPoint»</span></span><span> и «</span><span class="ui_item"><span>Экспорт истории в PDF»</span></span><span>.</span></p>
<h2>exportData</h2>
<p><span>Включает или отключает возможность экспорта данных из расширения визуализации и сохранения их в файле XLSX. Если задано значение </span><code><span class="syntax"><span>true</span></span></code><span>, параметр контекстного меню <strong>«</strong></span><strong><span class="ui_item">Экспорт данных»</span></strong><span> включен для расширения визуализации.</span></p>
<h2>Пример</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var ext = {
    support: {
        snapshot: true,
        export: true,
        exportData: true
    }
};</pre>
<h1>Property definitions</h1>
<h2>Array</h2>
<p>todo</p>
<h2>Integer</h2>
<p>todo</p>
<h2>Number</h2>
<p>todo</p>
<h2>String</h2>
<p>todo</p>
<h2>Text</h2>
<p>todo</p>
<h2>Button</h2>
<p>todo</p>
<h2>Button group</h2>
<p>todo</p>
<h2>Check box</h2>
<p>todo</p>
<h2>Color-picker</h2>
<p>todo</p>
<h2>Drop down list</h2>
<p>todo</p>
<h2>Link</h2>
<p>todo</p>
<h2>Media</h2>
<p>todo</p>
<h2>Radio button</h2>
<p>todo</p>
<h2>Slider</h2>
<p>todo</p>
<h2>Range-slider</h2>
<p>todo</p>
<h2>Switch</h2>
<p>todo</p>
<h2>Textarea</h2>
<p>todo</p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/">Обзор Extension API и описание методов расширений</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/overview-qlik-extension-api-and-methods/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
