<?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>Уровень 2 - Qlik Sense - Обучение, учебник, онлайн курс</title>
	<atom:link href="https://qliksense.ivan-shamaev.ru/category/qlik-level-2/feed/" rel="self" type="application/rss+xml" />
	<link>https://qliksense.ivan-shamaev.ru/category/qlik-level-2/</link>
	<description>Qlik Sense на русском языке. Пошаговые уроки для изучения Клик Сенс</description>
	<lastBuildDate>Mon, 01 May 2023 15:08:55 +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>Уровень 2 - Qlik Sense - Обучение, учебник, онлайн курс</title>
	<link>https://qliksense.ivan-shamaev.ru/category/qlik-level-2/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Доступ к системным данным и переменным в Extension Qlik Sense</title>
		<link>https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/</link>
					<comments>https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Mon, 21 Feb 2022 06:57:50 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Building visualization extensions]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[Methods Qlik Sense Extension API]]></category>
		<category><![CDATA[Доступ к системным переменным]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2759</guid>

					<description><![CDATA[<p>Введение Иногда при создании расширения Qlik Sense вам необходимо получить доступ к системным данным. Это особенно актуально, если вы пытаетесь создать что-то более общее. Возможно, вам нужен список полей, измерений или показателей. Или вам нужно работать с переменными. Если вы новичок в разработке Qlik Sense, вы можете посмотреть в документации<a class="moretag" href="https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/">Доступ к системным данным и переменным в Extension Qlik Sense</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Введение</h2>
<span>Иногда при создании расширения Qlik Sense вам необходимо получить доступ к системным данным. Это особенно актуально, если вы пытаетесь создать что-то более общее. Возможно, вам нужен список полей, измерений или показателей. Или вам нужно работать с переменными.</span>

<span>Если вы новичок в разработке Qlik Sense, вы можете посмотреть в документации по API способы получения необходимых данных. </span>

<strong><span>Не делай этого !!! <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f615.png" alt="😕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> </span></strong>

<span>Модель расширения Qlik Sense основана на идее использования одного универсального объекта, описанного в расширениях <strong>initialProperties</strong> и измененного пользователем на панели свойств и, возможно, программно (но это действительно продвинуто). Таким образом, хотя использование методов API для получения дополнительных данных является одним из описанных подходов в Qlik Help, которые используются в гибридных приложениях или веб-приложения и обращаются к данным Qlik, вам следует избегать их использования в mashups.</span>
<h2><span>Почему не нужно использовать вызовы API</span></h2>
<span>Если вы используете эти вызовы API в своем расширении, у вас возникнут проблемы:</span>
<ul>
 	<li><span>вы можете легко получить утечку памяти или «утечку общих объектов», когда вы создаете множество общих объектов.</span></li>
 	<li><span>вы можете легко потерять контроль над всеми работающими функциями обратного вызова при повторной проверке общих объектов.</span></li>
 	<li><span>если пользователь делает снимок, вызовы API будут обращаться к последней версии данных, а не к той, что в снимке, и, возможно, дадут неправильные данные.</span></li>
 	<li><span>если пользователь попытается экспортировать ваше расширение в PDF или Excel, оно может сломаться, поскольку служба, отвечающая за этот экспорт, не имеет доступа к оперативным данным, а только к моментальному снимку.</span></li>
</ul>
<span>Если вам абсолютно необходимо использовать эти вызовы, вы должны по крайней мере включить экспорт в PDF и Excel и не разрешать моментальные снимки вашего расширения.</span>
<h2><span>Что нужно делать вместо этого</span></h2>
<span>К счастью, есть альтернативы. Все эти вызовы API создают общие объекты, но, поскольку общий объект является очень гибкой структурой, вы можете фактически настроить общий объект за своим расширением для предоставления необходимых вам данных. Вот небольшая таблица того, что вы можете использовать:</span>
<table id="tablepress-1" class="tablepress tablepress-id-1">
<thead>
<tr class="row-1 odd">
<th class="column-1"><strong>Что нужно</strong></th>
<th class="column-2"><strong>Не использовать вызов API</strong></th>
<th class="column-3"><strong>Вместо этого добавьте в initialProperties</strong></th>
</tr>
</thead>
<tbody class="row-hover">
<tr class="row-2 even">
<td class="column-1">List of fields</td>
<td class="column-2">app.getList(&#8220;FieldList&#8221;)</td>
<td class="column-3">qFieldListDef</td>
</tr>
<tr class="row-3 odd">
<td class="column-1">List of measures</td>
<td class="column-2">app.getList(&#8220;MeasureList&#8221;)</td>
<td class="column-3">qMeasureListDef</td>
</tr>
<tr class="row-4 even">
<td class="column-1">List of dimensions</td>
<td class="column-2">app.getList(&#8220;DimensionList&#8221;)</td>
<td class="column-3">qDimensionListDef</td>
</tr>
<tr class="row-5 odd">
<td class="column-1">List of variables</td>
<td class="column-2">app.getList(&#8220;VariableList&#8221;)</td>
<td class="column-3">qVariableListDef</td>
</tr>
<tr class="row-6 even">
<td class="column-1">Variable value</td>
<td class="column-2">app.variable.getContent(..)</td>
<td class="column-3">qStringExpression or
qValueExpression</td>
</tr>
</tbody>
</table>
<span>Вы найдете рабочий пример этого в репозитории  </span><a href="https://github.com/erikwett/syslist"><span>расширении системного списка, </span></a><span> просто не используйте его, он предназначен как пример того, как получить данные и на самом деле не делает ничего полезного. Но возьмите ту часть initialProperties, которая вам нужна для расширения.</span>

<strong>Это выглядит так:</strong>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">initialProperties: {
                moneyFormat: {
                    qStringExpression: '=MoneyFormat'
                },
                qMeasureListDef: {
                    qType: "measure",
                    qData: {
                        qMeasure: "/qMeasure"
                    }
                },
                qDimensionListDef: {
                    qType: "dimension",
                    qData: {
                        qDim: "/qDim",
                        qDimInfos: "/qDimInfos"
                    }
                },
                qVariableListDef: {
                    qType: "variable",
                    qShowReserved: true,
                    qShowConfig: true
                },
                qFieldListDef: {
                    qShowSystem: true,
                    qShowHidden: true,
                    qShowSemantic: true,
                    qShowSrcTables: true,
                    qShowDerivedFields: true,
                    qShowImplicit: true
                }
            }</pre>
<h2><span>Когда следует использовать эти вызовы API</span></h2>
<span>Ну нет правил без исключений. Хотя вам следует избегать использования этих вызовов в API для части рендеринга вашего расширения, вы должны использовать их на своей панели свойств, если вам, например, нужно предоставить список полей пользователю. А в мэшапе они определенно очень полезны. И у API есть другие вызовы, которые вы, возможно, захотите использовать, но в основном это когда пользователь что-то делает, например, нажимает кнопку и т.д.</span>
<h2>Источник</h2>
https://extendingqlik.upper88.com/accessing-system-data-and-variables-in-a-qlik-sense-extension/<div class="learnpress"><div
	class="learn-press-message error lp-content-area">Этот шорткод LP Profile должен использоваться только на странице <a href="https://qliksense.ivan-shamaev.ru/wp-admin/admin.php?page=learn-press-settings"><strong>Учетная запись</strong></a></div>
</div>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/">Доступ к системным данным и переменным в Extension Qlik Sense</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/accessing-system-data-and-variables-in-a-qlik-sense-extension/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Как создать Qlik Sense Extension с помощью Nebula.js и React</title>
		<link>https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/</link>
					<comments>https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Sat, 12 Feb 2022 06:22:49 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Nebula.js React]]></category>
		<category><![CDATA[Qlik Sense Extension Nebula.js React]]></category>
		<category><![CDATA[Qlik Sense Extension with Nebula.js + React]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2708</guid>

					<description><![CDATA[<p>Обзор В этой серии руководств мы собираемся создать расширение Qlik Sense с использованием Nebula.js и React, используя Recharts библиотеки построения диаграмм React. Recharts — это компонуемая библиотека диаграмм компонентов React, созданная на основе D3. Вы можете найти домашнюю страницу библиотеки здесь . В частности, мы собираемся создать расширение диаграммы с областями на основе некоторых<a class="moretag" href="https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/">Как создать Qlik Sense Extension с помощью Nebula.js и React</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Обзор</h1>
<p><span>В этой серии руководств мы собираемся создать расширение Qlik Sense с использованием Nebula.js и React, используя Recharts библиотеки построения диаграмм React. Recharts — это компонуемая библиотека диаграмм компонентов React, созданная на основе D3. Вы можете найти домашнюю страницу библиотеки </span><a class="au kj" href="https://recharts.org/en-US" rel="noopener ugc nofollow" target="_blank"><span>здесь</span></a><span> .</span></p>
<p><span>В частности, мы собираемся создать расширение диаграммы с областями на основе некоторых данных о COVID-19. Я знаю, что это не супер оригинально, но он научит вас основам создания расширений в Nebula и React.</span></p>
<h1>Что вы узнаете</h1>
<ul>
<li><span>Как начать создавать расширения Qlik Sense с помощью Nebula.js и React</span></li>
<li><span>Основы хуков React</span></li>
<li><span>Библиотека графиков Recharts</span></li>
</ul>
<h1><span>Что вам нужно</span></h1>
<ul>
<li><span>Qlik Sense Desktop или Qlik Sense Business Cloud</span></li>
<li><span>Node и NPM, установленные на вашем компьютере, и редактор кода</span></li>
<li><span>Доступ к нашему стартовому проекту Nebula React доступен здесь: </span><a class="au kj" href="https://github.com/datalitlearning/nebula-react-starter" rel="noopener ugc nofollow" target="_blank"><span>https://github.com/datalitlearning/nebula-react-starter .</span></a></li>
</ul>
<h1>Проект github Nebula — React</h1>
<p><span>Загрузите стартовый проект Nebula — React с Github и запустите пользовательский интерфейс Nebula.js.</span></p>
<p><span>Мы создали стартовый проект, чтобы избежать головной боли при настройке. Это тесно связано с примером React на странице github Nebula.js. Чтобы начать работу с ним, перейдите к: <a href="https://github.com/datalitlearning/nebula-react-starter" target="_blank" rel="noopener">https://github.com/datalitlearning/nebula-react-starter</a></span></p>
<p><span>и либо клонируйте репо, либо загрузите. </span><span>Затем откройте папку с выбранным вами редактором кода и в терминале перейдите в корень вашего проекта и запустите…</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm i
npm run start</pre>
<p><span>Это запустит пользовательский интерфейс, в котором вы можете ввести свои учетные данные сайта Qlik Sense и подключиться к своему сайту. Он должен выглядеть примерно так, как показано ниже:</span></p>
<p>Подключение к вашему сайту Qlik Sense с помощью Nebula.js</p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1.png"><img fetchpriority="high" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1.png" alt="" width="1400" height="592" class="aligncenter size-full wp-image-2711" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1.png 1400w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1-300x127.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1-1024x433.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_1-768x325.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<p><span>Если у вас возникли проблемы с подключением к вашему сайту, щелкните знак вопроса, чтобы получить помощь (рядом с пунктом «Подключиться </span><strong class="jn hr"><span>к</span></strong><span> движку» в пользовательском интерфейсе), вам необходимо передать URL-адрес Websocket для подключения к вашему сайту Qlik.</span></p>
<p><span>После этого выберите приложение, и запустится среда разработки Nebula.js! <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span></p>
<blockquote><p><span>Обратите внимание, что в этом руководстве мы будем использовать данные COVD </span><span>-19. Я предварительно загрузил эти данные в qvf-файл, который можно найти в репозитории руководства на GitHub.</span></p></blockquote>
<p><span>Теперь ваша среда разработки nebula должна работать на </span><a class="au kj" href="http://localhost:8000/" rel="noopener ugc nofollow" target="_blank"><span>http://localhost:8000</span></a><span> и, по сути, представляет собой упрощенный пользовательский интерфейс Qlik, который вы можете использовать для разработки своего расширения. При первом запуске вы получите бесконечно работающий счетчик, который выглядит примерно так:</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2.png"><img decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2.png" alt="" width="1400" height="570" class="aligncenter size-full wp-image-2712" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2.png 1400w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2-300x122.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2-1024x417.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_2-768x313.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<p><span>Не идеально. Nebula.js ищет ваше скомпилированное расширение, но пока ничего не может найти. Давайте исправим это, открыв </span><strong class="jn hr"><span>новое</span></strong><span> окно терминала и перейдя в корень вашего проекта туманности…</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">cd nebula-react-starter</pre>
<p>далее запускаем сборку</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run build</pre>
<p><span>Ура, теперь мы должны увидеть наше расширение в пользовательском интерфейсе Nebula.js…</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3.png"><img decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3.png" alt="" width="1400" height="610" class="aligncenter size-full wp-image-2713" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3.png 1400w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3-300x131.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3-1024x446.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_3-768x335.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<p><span>Теперь мы запускаем наш сервер разработки ( </span><em class="mm"><span>через npm run start</span></em><span> ) и перекомпилируем наше расширение при внесении изменений ( </span><em class="mm"><span>через npm run build</span></em><span> ).</span></p>
<p><span>Вы также заметите, что у нас есть новая папка </span><strong class="jn hr"><em class="mm"><span>dist</span></em></strong><span> в наших файлах проекта, она содержит наш скомпилированный код расширения, который подбирает nebula.</span></p>
<h1><span>Проверка файлов проекта</span></h1>
<p><span>Прежде чем мы углубимся в программирование, давайте просмотрим файлы проекта, чтобы у вас было понимание кода:</span></p>
<p><strong class="jn hr"><span>data.js:</span></strong><span> содержит цель данных. Цель данных — это способ определить, где находятся динамические HyperCubeDef в свойствах универсального объекта. Для каждого целевого объекта данных вы можете указать дополнительные сведения, такие как максимальное/минимальное количество измерений и показателей.</span></p>
<p><strong class="jn hr"><span>extDefinition.js</span></strong><span> : он будет содержать определение нашей панели свойств, но пока должен быть пустым объектом.</span></p>
<p><strong class="jn hr"><span>object-properties.js</span></strong><span> : свойства нашего расширения, включая выборку данных HyperCube и те, которые мы хотим изменить с помощью нашей панели свойств.</span></p>
<p><strong class="jn hr"><span>root.jsx</span></strong><span> : это точка входа в реакцию. Мы экспортируем функцию </span><strong class="jn hr"><em class="mm"><span>рендеринга</span></em><span> </span></strong><span>, которая содержит метод рендеринга React DOM. Он отображает JSX в </span><strong class="jn hr"><em class="mm"><span>переменную элемента</span></em></strong><span> , которая передается в функцию при вызове в index.js.</span></p>
<p><strong class="jn hr"><span>index.js</span></strong><span> : здесь отображается наше расширение. Чтобы отобразить что-то, что вам нужно для доступа к элементу DOM, которому назначена визуализация, вы можете сделать это, импортировав </span><code class="ms mt mu mv ma b">useElement</code><span>функцию. Это то, что мы передали нашему методу рендеринга ReactDOM, чтобы отобразить наш JSX в нашем расширении.</span></p>
<h1><span>Начало работы с Recharts</span></h1>
<p><span>Я бы порекомендовал вам прочитать руководство по началу работы с Rechart, </span><a class="au kj" href="https://recharts.org/en-US/guide/getting-started" rel="noopener ugc nofollow" target="_blank"><span>прежде</span></a><span> чем мы начнем, чтобы у вас было некоторое представление о библиотеке.</span></p>
<p><span>Мы начнем с установки библиотеки и импорта компонентов в наш проект.</span></p>
<p><span>В терминальном режиме</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install recharts --save</pre>
<p><span>Затем создайте новый файл с именем </span><strong class="jn hr"><span>App.jsx</span></strong><span> в папке </span><strong class="jn hr"><span>src . </span></strong><span>В этом компоненте мы импортируем и визуализируем диаграмму областей Recharts, используя некоторые фиктивные данные. Нам также нужно импортировать и отображать </span><strong class="jn hr"><span>App.jsx</span></strong><span> в нашем файле </span><strong class="jn hr"><span>root.jsx</span></strong><span> .<br />
</span></p>
<p><span style="color: #ff6600;"><strong>App.jsx</strong></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import React from 'react';
import { XAxis, YAxis, AreaChart, Area } from 'recharts';

const App = () =&gt; {

  const data = [
    {
      name: 'Page A', uv: 4000, pv: 2400, amt: 2400,
    },
    {
      name: 'Page B', uv: 3000, pv: 1398, amt: 2210,
    },
    {
      name: 'Page C', uv: 2000, pv: 9800, amt: 2290,
    },
    {
      name: 'Page D', uv: 2780, pv: 3908, amt: 2000,
    },
    {
      name: 'Page E', uv: 1890, pv: 4800, amt: 2181,
    },
    {
      name: 'Page F', uv: 2390, pv: 3800, amt: 2500,
    },
    {
      name: 'Page G', uv: 3490, pv: 4300, amt: 2100,
    },
  ];

  return (
    &lt;AreaChart 
      width={500}
      height={400}
      data={data}
    &gt;
      &lt;XAxis dataKey="name" /&gt;
      &lt;YAxis /&gt;
      &lt;Area type="monotone" dataKey="uv" stackId="1" stroke="#8884d8" fill="#8884d8" /&gt;
      &lt;Area type="monotone" dataKey="pv" stackId="1" stroke="#82ca9d" fill="#82ca9d" /&gt;
      &lt;Area type="monotone" dataKey="amt" stackId="1" stroke="#ffc658" fill="#ffc658" /&gt;
    &lt;/AreaChart&gt;
  );
};

export default App;</pre>
<p><span style="color: #ff6600;"><strong>root.jsx</strong></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import ReactDOM from 'react-dom';
import React from 'react';
import App from './App'

export function render(element) {
  ReactDOM.render(&lt;App /&gt;, element);
}

export function teardown(element) {
  ReactDOM.unmountComponentAtNode(element);
}</pre>
<p><span>Теперь вы должны увидеть диаграмму, отображаемую в нашем пользовательском интерфейсе Nebula. Давайте уделим минуту, чтобы просмотреть код в </span><strong class="jn hr"><span>App.jsx</span></strong><span> и понять следующие шаги для нашего расширения.</span></p>
<p><span>Обратите внимание, переменная данных, которую мы передаем в нашу диаграмму с областями, представляет собой массив объектов. Это отличается от структуры наших данных HyperCube, поэтому нам нужно будет манипулировать данными из нашего HyperCube, прежде чем передавать их в нашу диаграмму позже в этом руководстве.</span></p>
<p><span>Также обратите внимание, как мы отображаем данные на нашей диаграмме. Мы создаем отдельные компоненты </span><strong class="jn hr"><span>Area</span></strong><span> для каждой области рендеринга. Мы хотим, чтобы наше расширение поддерживало несколько мер, поэтому нам нужно будет создать ряд этих компонентов области в зависимости от количества мер, созданных в нашем расширении.</span></p>
<h1><span>Использование данных из нашего HyperCube</span></h1>
<p><span>Теперь мы возьмем данные из нашего гиперкуба и вставим их в нашу диаграмму. Для этого мы будем использовать пользовательские хуки Nebula для получения данных из нашего движка.</span></p>
<blockquote><p><strong><span>Пользовательские хуки</span></strong><span> — это механизм повторного использования логики с отслеживанием состояния.</span></p></blockquote>
<p><span>В </span><strong><span>index.js</span></strong><span> мы уже используем два таких хука: </span><strong><span>useElement</span></strong><span> (который получает HTMLElement, в который визуализируется эта визуализация) и </span><strong class="jn hr"><span>useLayout</span></strong><span> (который получает макет универсального объекта, связанного с этой визуализацией).</span></p>
<p><span>Гиперкуб Qlik находится в макете универсального объекта, поэтому давайте сохраним его в переменной с именем </span><strong><span>hc:</span></strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">const hc = layout.qHyperCube</pre>
<p><span>Теперь ваш </span><strong><span>index.js</span></strong><span> должен выглядеть так, как показано ниже. Вы также можете консольно войти в </span><strong><span>hc</span></strong><span> и посмотреть на объект в консоли. Обратите внимание, что когда вы добавляете в расширение </span><strong class="jn hr"><span>измерения</span></strong><span> и </span><strong class="jn hr"><span>меры</span></strong><span> , данные в </span><strong class="jn hr"><span>hc.qDataPages[0].qMatrix</span></strong><span> обновляются.</span></p>
<p><span style="color: #ff6600;"><strong>index.js</strong></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import { useElement, useLayout, useEffect } from '@nebula.js/stardust';
import properties from './object-properties';
import data from './data';
import { render } from './root';

export default function supernova() {
  return {
    qae: {
      properties,
      data,
    },
    component() {
      const el = useElement();
      const layout = useLayout();
      const hc = layout.qHyperCube;

      useEffect(() =&gt; {
        // do some heavy update
      }, [layout]);

      render(el, hc, { layout });
    },
  };
}</pre>
<p><span>Если мы посмотрим на данные </span><strong class="jn hr"><span>HyperCube</span></strong><span> более подробно, то увидим, что он возвращает массив массивов, который содержит объект для каждого измерения/меры, которые мы добавляем в наш </span><strong class="jn hr"><span>HyperCube</span></strong><span> . Нам нужно преобразовать это в структуру данных, которую ожидает Recharts, чтобы заполнить диаграмму.</span></p>
<p><span>Как видно из примера данных выше, нам нужно создать массив объектов, где каждый объект содержит наши данные в парах ключ-значение. Прежде чем мы продолжим и сделаем это, давайте рассмотрим хуки React.</span></p>
<h1><span>Объясняя Nebula / React Hooks</span></h1>
<p><span>Прежде чем мы перейдем к коду, я хочу объяснить использование хуков в проектах Nebula и React. Хуки были представлены в React 16.8 и позволяют нам использовать состояние и другие функции React в функциональных компонентах.</span></p>
<p><span>Хуки имеют ряд преимуществ, включая упрощение повторного использования логики с сохранением состояния между компонентами и упрощение нашего кода React (нам больше не нужно использовать компоненты класса!)</span></p>
<p><span>Библиотека Nebula.js создала реализацию в стиле хуков для реализации повторно используемой логики.</span></p>
<blockquote><p><span>В то время как реализация Nebula полностью настраиваема с помощью пользовательских хуков, концепция и правила очень похожи, настолько, что вы можете прочитать </span><a class="au kj" href="https://reactjs.org/docs/hooks-intro.html" rel="noopener ugc nofollow" target="_blank"><span>документацию по хукам React</span></a><span> , чтобы понять, как использовать собственные хуки stardust.</span></p></blockquote>
<p><span>На самом деле, Nebula.js поставляется со своими собственными хуками </span><code class="ms mt mu mv ma b">useState</code><span>&amp; </span><code class="ms mt mu mv ma b">useEffect</code><span>, которые выполняют те же функции, что и собственные хуки React. Все пользовательские хуки в библиотеке Nebula можно найти </span><a class="au kj" href="https://qlik.dev/apis/javascript/nebulajs-stardust" rel="noopener ugc nofollow" target="_blank"><span>здесь</span></a><span> (с префиксом </span><strong class="jn hr"><span>use</span></strong><span> ).</span></p>
<h1><span>Вернемся к нашему коду..</span></h1>
<p><span>Чтобы манипулировать нашими данными из HyperCube, нам нужно использовать </span><code class="ms mt mu mv ma b">useEffect</code><span>хук Nebula.js.</span></p>
<blockquote><p><span>Переданная функция </span><code>useEffect</code><span>будет запущена после того, как рендеринг будет зафиксирован на экране.</span></p>
<p><span>По умолчанию эффекты запускаются после каждого завершенного рендеринга, но вы можете активировать их </span><a href="https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect" rel="noopener ugc nofollow" target="_blank"><span>только при изменении определенных значений</span></a><span> .</span></p></blockquote>
<p><span>В этом случае мы будем запускать функцию, которая перебирает наш HyperCube и реструктурирует данные.</span></p>
<p><strong class="jn hr"><span>Для начала мы собираемся принудительно выбрать одно измерение и две меры в нашем расширении</span></strong><span> . На данный момент это упростит код, и мы вернемся к этому в следующем руководстве.</span></p>
<p><span>Мы можем установить это в нашем файле </span><strong class="jn hr"><span>data.js</span></strong><span> , который теперь должен выглядеть следующим образом:</span></p>
<p><span style="color: #ff6600;"><strong>data.js</strong></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default {
  targets: [{
    path: '/qHyperCubeDef',
    dimensions: {
      min: 1,
      max: 1,
    },
    measures: {
      min: 2,
      max: 2,
    },
  }]
};</pre>
<p><span>Мы также собираемся добавить следующий код в раздел </span><strong class="jn hr"><span>компонентов нашей функции </span></strong><span></span><strong class="jn hr"><span>supernova</span></strong><span> в нашем файле </span><strong class="jn hr"><span>index.js</span></strong><span> .</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">// state to store our data used in our chart
const [seriesData, setSeriesData] = useState([]);

//state to store our dimension name
const [dimKey, setDimKey] = useState([]);

//state to store our measure names
const [measKeys, setMeasKeys] = useState([]);

//Array that we will populate with restructured data
const series = [];</pre>
<p><span>Мы создаем 3 набора начального состояния для хранения:</span></p>
<ul>
<li><span>Реструктурированные данные, которые мы передадим на нашу диаграмму</span></li>
<li><span>Массив, содержащий имя нашего измерения</span></li>
<li><span>Массив, содержащий имена наших мер</span></li>
</ul>
<p><span>На данный момент мы будем хранить имена наших измерений и показателей в виде массива, чтобы его было легче расширить, когда мы разрешим больше показателей.</span></p>
<p><span>Далее, в нашем </span><code class="ms mt mu mv ma b"></code><em class="mm">useEffect</em><span>, мы сопоставим наши данные HyperCube и создадим новый массив объектов. Обратите внимание, что в приведенном ниже коде мы сопоставляем </span><strong class="jn hr"><span>hcData</span></strong><span> и помещаем объекты в массив с именем </span><strong class="jn hr"><span>series</span></strong><span> , который содержит данные измерений и измерений в парах ключ-значение.</span></p>
<p><span>Затем мы помещаем эти данные вместе с именами измерений и показателей в состояние (строки 41–43 ниже). Наконец, мы добавляем эти данные в наш метод рендеринга и в </span><strong class="jn hr"><span>root.jsx</span></strong><span> передаем их в наш компонент приложения.</span></p>
<p><strong>index.js</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import { useElement, useLayout, useEffect, useState } from '@nebula.js/stardust';
import properties from './object-properties';
import data from './data';
import { render } from './root';

export default function supernova() {
  return {
    qae: {
      properties,
      data,
    },
    component() {

      const el = useElement();
      const layout = useLayout();
      
      const [seriesData, setSeriesData] = useState([]);
      const [dimKey, setDimKey] = useState([]);
      const [measKeys, setMeasKeys] = useState([]);
      const hc = layout.qHyperCube;
      const series = [];

      useEffect(() =&gt; {

        const hcData =  hc.qDataPages[0].qMatrix;

        if (hcData.length === 0) return;

        const dim1 = hc.qDimensionInfo[0].qFallbackTitle;
        const meas1 = hc.qMeasureInfo[0].qFallbackTitle;
        const meas2 = hc.qMeasureInfo[1].qFallbackTitle;

        hcData.map(e =&gt; (
          series.push({
            [dim1]: e[0].qText,
            [meas1]: e[1].qText,
            [meas2]: e[2].qText,
          })
        ));

        setDimKey([dim1]);
        setMeasKeys([meas1, meas2]);
        setSeriesData(series);
      }, [layout]);

      render(el, seriesData, dimKey, measKeys, { layout });
    },
  };
}</pre>
<p><span style="color: #ff6600;"><strong>root.jsx</strong></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import ReactDOM from 'react-dom';
import React from 'react';
import App from './App'

export function render(element, seriesData, dimKey, measKeys) {
  ReactDOM.render(&lt;App data={seriesData} dimKey={dimKey} measKeys={measKeys} /&gt;, element);
}

export function teardown(element) {
  ReactDOM.unmountComponentAtNode(element);
}</pre>
<p><span>Итак, теперь у нас есть все данные, которые нам нужны в </span><strong class="jn hr"><span>App.jsx</span></strong><span> . Теперь мы можем передать это в нашу диаграмму. Если вы помните ранее в этом руководстве, мы заметили, что нам нужно визуализировать компонент </span><strong class="jn hr"><span>AreaChart</span></strong><span> для каждой визуализируемой линии/области. Для этого нам нужно сопоставить наши ключи измерений.</span></p>
<p><span>Мы также создали массив цветов для окрашивания наших компонентов Area.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import React from 'react';
import { XAxis, YAxis, AreaChart, Area } from 'recharts';

const App = ({ data, dimKey, measKeys }) =&gt; {

  const colors = ["#8884d8", "#82ca9d"]

  return (
    &lt;AreaChart 
      width={500}
      height={400}
      data={data}
    &gt;
      &lt;XAxis dataKey={dimKey[0]} /&gt;
      &lt;YAxis /&gt;
      {measKeys.map((m, i) =&gt; (
        &lt;Area type="monotone" dataKey={m} stackId="1" stroke={colors[i]} fill={colors[i]} /&gt;
      ))}
    &lt;/AreaChart&gt;
  );
};

export default App;</pre>
<p><span>Теперь ваше расширение должно выглядеть примерно так, как показано ниже.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4.png" alt="" width="1400" height="592" class="aligncenter size-full wp-image-2714" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4.png 1400w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4-300x127.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4-1024x433.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_4-768x325.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<h1><span>Делаем нашу диаграмму отзывчивой</span></h1>
<p><span>Мы закончим написание кода в части 1 этого руководства, сделав нашу диаграмму адаптивной. К счастью, у Recharts есть хороший и простой пример этого на своем сайте, который можно найти </span><a class="au kj" href="https://recharts.org/en-US/examples/AreaResponsiveContainer" rel="noopener ugc nofollow" target="_blank"><span>здесь</span></a><span> .</span></p>
<p><span>Они предоставляют компонент </span><strong class="jn hr"><span>ResonsiveContainer</span></strong><span> , который мы можем обернуть вокруг нашей диаграммы, чтобы она расширялась до размера своего родительского контейнера. Мы можем импортировать и использовать это в </span><strong class="jn hr"><span>App.jsx</span></strong><span> , как показано ниже.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import React from 'react';
import { XAxis, YAxis, AreaChart, Area, ResponsiveContainer } from 'recharts';

const App = ({ data, dimKey, measKeys }) =&gt; {

  const colors = ["#8884d8", "#82ca9d"]

  return (
    &lt;div style={{ width: '100%', height: '100%' }}&gt;
      &lt;ResponsiveContainer&gt;
        &lt;AreaChart 
          width={500}
          height={400}
          data={data}
        &gt;
          &lt;XAxis dataKey={dimKey[0]} /&gt;
          &lt;YAxis /&gt;
          {measKeys.map((m, i) =&gt; (
            &lt;Area type="monotone" dataKey={m} stackId="1" stroke={colors[i]} fill={colors[i]} /&gt;
          ))}
        &lt;/AreaChart&gt;
      &lt;/ResponsiveContainer&gt;
    &lt;/div&gt;
  );
};

export default App;</pre>
<h1><span>Тестирование расширения в Qlik Sense</span></h1>
<p><span>Наконец, мы быстро импортируем наше расширение в Qlik Sense и убедимся, что все работает должным образом.</span></p>
<p><span>Запустите приведенную ниже команду в своем терминале</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run sense</pre>
<p><span>Это поместит наше расширение в новую папку в нашем репозитории. Затем мы можем заархивировать это и импортировать на наш сайт Qlik Sense.</span></p>
<p><span>И вот, у нас есть рабочее расширение, созданное с использованием Nebula.js и React!</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5.png" alt="" width="1400" height="766" class="aligncenter size-full wp-image-2715" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5.png 1400w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5-300x164.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5-1024x560.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/build_qlik_sense_extension_with_nebula_js_react_5-768x420.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<h1>Использованные материалы при создании статьи</h1>
<ul>
<li><a href="https://medium.com/data-lit/lets-build-a-qlik-sense-extension-with-nebula-js-react-part-1-2-44909ab15a47" target="_blank" rel="noopener">https://medium.com/data-lit/lets-build-a-qlik-sense-extension-with-nebula-js-react-part-1-2-44909ab15a47</a></li>
</ul>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/">Как создать Qlik Sense Extension с помощью Nebula.js и React</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/build-a-qlik-sense-extension-with-nebula-js-react/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Создание Extension с помощью qlik nebula.js и D3.js &#8211; Tutorial</title>
		<link>https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/</link>
					<comments>https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Fri, 11 Feb 2022 21:12:38 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[nebula.js extension]]></category>
		<category><![CDATA[qlik nebula.js и D3.js - Tutorial]]></category>
		<category><![CDATA[Создание Extension nebula.js и D3.js]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2695</guid>

					<description><![CDATA[<p>Создание расширенного расширения визуализации с использованием Qlik Nebula.js и D3.js &#160; В моем последнем посте я рассказал о надежных возможностях API-интерфейсов Qlik Sense (QS) для создания готовых визуальных метафор и способах их интеграции в экосистему Qlik. Естественным выбором для разработчиков при создании расширений QS на протяжении многих лет был Extension API , в  основном использующий<a class="moretag" href="https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/">Создание Extension с помощью qlik nebula.js и D3.js &#8211; Tutorial</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Создание расширенного расширения визуализации с использованием Qlik Nebula.js и D3.js</h1>
<p><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bed_4-1618230383486.png"><span class="lia-message-image-wrapper lia-message-image-actions-narrow lia-message-image-actions-below"><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1.jpg" alt="" width="1280" height="550" class="aligncenter size-full wp-image-2702" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1.jpg 1280w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1-300x129.jpg 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1-1024x440.jpg 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_1-768x330.jpg 768w" sizes="(max-width: 1280px) 100vw, 1280px" /></a></span></span></p>
<p>&nbsp;</p>
<p class="graf graf--p"><span>В моем последнем </span><a href="https://dipankar-tnt.medium.com/leveraging-the-power-of-qlik-sense-saas-extension-api-to-design-a-new-visualization-chart-d54194aaf07f" target="_blank" rel="noopener nofollow noreferrer"><span>посте</span></a><span> я рассказал о надежных возможностях API-интерфейсов Qlik Sense (QS) для создания готовых визуальных метафор и способах их интеграции в экосистему Qlik. Естественным выбором для разработчиков при создании расширений QS на протяжении многих лет был </span><a href="https://qlik.dev/apis/javascript/extensions" target="_blank" rel="noopener nofollow noreferrer"><span>Extension API</span></a><span> , в  основном использующий ванильный JavaScript, jQuery и AngularJS.</span></p>
<blockquote><p><em><span>API расширений состоит из методов и свойств, используемых для создания пользовательских расширений визуализации.</span></em></p></blockquote>
<p class="graf graf--p"><span>Введите… Решение Qlik Sense с открытым исходным кодом — <a href="https://qlik.dev/apis/javascript/nebulajs-stardust" target="_blank" rel="noopener nofollow noreferrer"><strong>Nebula.js</strong></a> !</span><a href="https://qlik.dev/apis/javascript/nebulajs-stardust" target="_blank" rel="noopener nofollow noreferrer"> <strong><span></span></strong></a><span></span></p>
<p><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bed_0-1618228754457.png"><span class="lia-message-image-wrapper lia-message-image-actions-narrow lia-message-image-actions-below"><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2.png" alt="" width="1064" height="330" class="aligncenter size-full wp-image-2703" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2.png 1064w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2-300x93.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2-1024x318.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_2-768x238.png 768w" sizes="(max-width: 1064px) 100vw, 1064px" /></a></span></span></p>
<p>&nbsp;</p>
<p class="graf graf--p"><span>Nebula.js — это набор библиотек и API-интерфейсов JavaScript, не зависящих от продукта и платформы, которые помогают разработчикам интегрировать визуализации и гибридные приложения поверх Qlik Associative Engine в QS Desktop, QS Enterprise для Windows и SaaS-редакции Qlik Sense. Это руководство специально относится к </span><strong><span>выпуску QS SaaS. </span></strong><span>Nebula.js предлагает разработчикам альтернативу API возможностей, которые исторически использовались для создания гибридных приложений. В этом руководстве основное внимание будет уделено разработке новой визуализации на основе пользовательского сценария с использованием Nebula.js и сторонней библиотеки визуализации D3.js. Наша цель — понять, как мы можем использовать Nebula.js для создания объекта расширения QS и реализации готовых возможностей визуализации в рамках </span><strong><span>SaaS .</span></strong><span>Платформа. В этом руководстве не делается акцент на части программирования D3.js, но обсуждается мотивация визуализации.</span></p>
<p class="graf graf--p"><span style="font-size: large;"><strong><span>Сценарий пользователя.</span></strong><span> </span></span><em><span>У организации, использующей Qlik Sense, есть новое требование по разработке визуального представления для понимания многомерного многомерного набора данных для своей организации. Их набор данных состоит из числовых значений, и они хотят сравнить несколько объектов вместе, чтобы проанализировать отношения между ними. Основываясь на этих требованиях, их инженер по визуализации данных представляет им «график с параллельными координатами». </span></em></p>
<h3 id="toc-hId-1261881332"><strong><span>Итак, что такое график с параллельными координатами?</span></strong></h3>
<blockquote><p><em><span>Графики с параллельными координатами (PCP) доказали свою эффективность при эффективной визуализации многомерных наборов данных высокой размерности. В параллельных координатах каждый объект представлен в виде вертикальных полос, а значения нанесены на график в виде серии линий, соединенных по каждой оси. Их преимущество в том, что вертикальные полосы (функции) могут иметь собственный масштаб, поскольку каждая функция работает с разными единицами измерения. PCP дает представление о конкретных скрытых закономерностях в данных, таких как сходства, кластеры и т. д., и позволяет проводить более простой сравнительный анализ.</span></em></p></blockquote>
<h4 id="toc-hId-1952442806"><span style="font-size: medium;"><span>Основываясь на требованиях, давайте начнем с создания расширения QS с помощью </span><a href="https://github.com/qlik-oss/nebula.js" target="_blank" rel="noopener nofollow noreferrer"><span>Nebula.js</span></a><span> .</span></span></h4>
<p class="graf graf--p"><strong><span>Предпосылки</span></strong><span> :</span></p>
<ol>
<li><span>Node.js (версия 10 или новее)</span></li>
<li><span>Терминал (например, Git Bash в Windows или Terminal в Mac)</span></li>
<li><span>IDE на ваш выбор, например, VS Code.</span></li>
<li><span>Существующая </span><a href="https://qlik.dev/basics/authentication-options#web-integrations" target="_blank" rel="noopener nofollow noreferrer"><span>веб-интеграция</span></a><span> или возможность ее создания в вашем арендаторе (особенно это относится к выпуску QS SaaS).</span></li>
<li><span>Приложение Qlik Sense с данными.</span></li>
</ol>
<p class="graf graf--p"><u><strong><span>Шаг 1:</span></strong></u><span> Используйте интерфейс командной строки nebula.js для импорта необходимых пакетов. Команда помещает проект в папку /hello со следующей структурой:</span></p>
<ul>
<li><span>/src</span></li>
<li><span>index.js — основная точка входа этой визуализации</span></li>
<li><span>object-properties.js — свойства объекта, хранящиеся в приложении.</span></li>
<li><span>data.js — конфигурация данных</span></li>
<li><span>/test &#8211; Интеграционные тесты</span></li>
<li>package.json</li>
</ul>
<p class="graf graf--p"><strong><span>Команда</span></strong><span>:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npx @nebula.js/cli create hello --picasso none</pre>
<p class="graf graf--p"><u><strong><span>Шаг 2:</span></strong></u><span> Запустите сервер разработки, выполнив:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">cd hello
npm run start</pre>
<p class="graf graf--p"><span>Команда запускает локальный сервер разработки и открывает </span><a href="http://localhost:8080/" target="_blank" rel="noopener nofollow noreferrer"><span>http://localhost:8080</span></a><span> в вашем браузере. Преимущество наличия сервера разработки с Nebula.js заключается в том, что он предоставляет интерактивный способ тестирования и редактирования вашего расширения без необходимости итеративного развертывания в QS каждый раз, когда вносится новое изменение.</span></p>
<p class="graf graf--p"><u><strong><span>Шаг 3:</span></strong></u><span> Настройте структуру данных.</span></p>
<p class="graf graf--p"><span>Визуализации в QS основаны на определении гиперкуба (qHyperCubeDef). Следовательно, любой новый визуальный объект, который мы хотим добавить в экосистему QS, должен иметь определенную структуру данных. В Nebula.js у нас есть файл object-properties.js, который позволяет определить структуру нашего объекта.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">const properties = {
  showTitles: true,
  qHyperCubeDef: {
    qInitialDataFetch: [{ qWidth: 30, qHeight: 200 }],
  }
}</pre>
<p class="graf graf--p"><span>Нам также необходимо установить цель данных в файле data.js, чтобы мы ссылались на правильное определение гиперкуба (важно отметить, если у вас есть несколько объектов qHyperCubeDef).</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default {
  targets: [
    {
      path:'/qHyperCubeDef',
    }
  ],
};</pre>
<p class="graf graf--p"><u><strong><span>Шаг 4:</span></strong></u><span> Разработка расширения визуализации с использованием Nebula.js и D3.js.</span></p>
<p class="graf graf--p"><strong><span>Специальный код QS Nebula.js:</span></strong></p>
<p class="graf graf--p"><span>Теперь, когда у нас все готово, мы начинаем разработку нашего расширения с пользовательским объектом визуализации, используя файл index.js из нашего проекта.</span></p>
<p class="graf graf--p"><span>Обратите внимание, что Nebula.js и его основной пакет @nebula.js/stardust построены на концепции настраиваемых </span><em><span>хуков</span></em><span> . Это может показаться знакомым людям, работающим с </span><a href="https://reactjs.org/" target="_blank" rel="noopener nofollow noreferrer"><span>React.js</span></a><span> . </span><em><span>Хуки</span></em><span> — это концепция, которая делает упор на повторно используемые составные функции, а не на классические объектно-ориентированные классы и наследование. Основные хуки, от которых мы зависим при разработке нашего объекта расширения, описаны ниже:</span></p>
<p><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bed_1-1618229164557.png"><span class="lia-message-image-wrapper lia-message-image-actions-narrow lia-message-image-actions-below"></span></span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3.png" alt="" width="1600" height="363" class="aligncenter size-full wp-image-2704" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3.png 1600w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3-300x68.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3-1024x232.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3-768x174.png 768w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_3-1536x348.png 1536w" sizes="(max-width: 1600px) 100vw, 1600px" /></a></p>
<p class="graf graf--p"><span>Метод, который помогает нам визуализировать наш объект визуализации, — это функция component(). Функция component() выполняется каждый раз, когда что-то, связанное с рендерингом объекта, изменяется, например, тема, модель данных, выборка данных, состояние компонента и т. д. Эту функцию можно сравнить с функцией paint() в Extension API.</span></p>
<p class="graf graf--p"><span>Чтобы отобразить наши данные, нам сначала нужно получить доступ к </span><em><span>макету</span></em><span> через хук useLayout, а затем использовать его в сочетании с хуком useEffect. qDataPages </span><strong><span>[0].qMatrix гиперкуба</span></strong><span> содержит все данные (измерения и меры), используемые в среде QS, и нам нужно будет передать эти данные в нашу визуализацию на основе D3.js.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">component() {
      const element = useElement();
      const layout = useLayout();
      useEffect(() =&gt; {
        var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix;
      }
                }</pre>
<p class="graf graf--p"><span>Чтобы увидеть значения данных и понять структуру </span><em><span>qHyperCube</span></em><span> , всегда полезно выполнить </span><em><span>console.log(layout)</span></em><span> . Фрагмент показывает значения, характерные для нашего варианта использования. Каждый раз, когда к нашему объекту расширения добавляется новое измерение или мера, </span><strong><span>qDataPages[0].qMatrix</span></strong><span> обновляется этими значениями </span><strong><span>.</span></strong></p>
<p><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bed_2-1618229293997.png"><span class="lia-message-image-wrapper lia-message-image-actions-narrow lia-message-image-actions-below"></span></span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4.png" alt="" width="1070" height="538" class="aligncenter size-full wp-image-2705" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4.png 1070w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4-300x151.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4-1024x515.png 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js_4-768x386.png 768w" sizes="(max-width: 1070px) 100vw, 1070px" /></a></p>
<p class="graf graf--p"><span>Затем необходимые значения </span><em><span>измерения</span></em><span> для нашей диаграммы извлекаются из гиперкуба с использованием свойства </span><strong><span>qText</span></strong><span> из </span><strong><span>qDataPages[0].qMatrix,</span></strong><span> как показано ниже.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">var data = qMatrix.map(function (d) {
          return {
            PetalLength: d[0].qText,
            PetalWidth: d[1].qText,
            SepalLength: d[2].qText,
            SepalWidth: d[3].qText,
            Species: d[4].qText,
          };
        });</pre>
<p class="graf graf--p"><span>Наш следующий шаг — определить ширину и высоту объекта визуализации и зафиксировать его </span><em><span>идентификатор</span></em><span> . Мы будем использовать этот </span><em><span>идентификатор</span></em><span> для привязки его к нашему объекту </span><em><span>элемента</span></em><span> из хука useLayout, как показано ниже:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">var width = 1000;
var height = 400;
var id = "container_" + layout.qInfo.qId;
const elem_new = `&lt;div id=${id}&gt;&lt;/div&gt;`;
element.innerHTML = elem_new;</pre>
<p class="graf graf--p"><span>Наконец, мы вызываем функцию D3.js из хука useEffect.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">viz(data, width, height, id);</pre>
<p class="graf graf--p"><strong><span>Специальный код D3.js:</span></strong></p>
<p class="graf graf--p"><span>Функция viz() содержит весь наш код D3.js, который позволяет нам рисовать график в параллельных координатах. Во-первых, нам нужно добавить SVG к </span><em><span>&lt;div&gt;</span></em><span> , содержащему </span><em><span>идентификатор</span></em><span> нашего объекта QS, как показано ниже.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">var svg = d3
          .select("#" + id)
          .append("svg")
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)
          .append("g")
          .attr(
            "transform",
            "translate(" + margin.left + "," + margin.top + ")"
          );</pre>
<p class="graf graf--p"><span>Затем мы получаем все измерения, кроме </span><em><span>Species</span></em><span> , для построения наших осей x и y.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">var dimensions = Object.keys(data[0]).filter(function (d) {
          return d != "Species";
        });

        var y = {};
        for (var i in dimensions) {
          var name_new = dimensions[i];
          y[name_new] = d3.scaleLinear().domain([0, 8]).range([height, 0]);
        }

        var x = d3.scalePoint().range([0, width]).domain(dimensions);</pre>
<p class="graf graf--p"><span>Чтобы нарисовать линии для нашего графика с параллельными координатами, нам нужно построить функцию </span><em><span>пути , которая будет брать строку из нашего </span></em><span></span><em><span>qHyperCube</span></em><span> и возвращать координаты </span><em><span>x</span></em><span> и </span><em><span>y</span></em><span> линии.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">function path(d) {
          return d3.line()(
            dimensions.map(function (p) {
              return [x(p), y[p](d[p])];
            })
          );
        }</pre>
<p class="graf graf--p"><span>И, наконец, мы связываем все с нашим SVG, как показано ниже:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">svg
  .selectAll("myPath")
  .data(data)
  .enter()
  .append("path")
  .attr("class", function (d) {
    return "line " + d.Species;
  })
  .attr("d", path)
  .style("fill", "none")
  .style("stroke", function (d) {
    return color(d.Species);
  })
  .style("opacity", 0.5);</pre>
<p class="graf graf--p"><strong><u><span>Шаг 5:</span></u><span> Развертывание расширения.</span></strong></p>
<p class="graf graf--p"><span>Чтобы создать наш проект, мы используем приведенную ниже команду, чтобы сгенерировать все файлы, доступные для чтения QS, и поместить их в папку /hello-ext. Затем эту папку можно сжать (.zip) и загрузить в раздел </span><em><span>расширений</span></em><span> консоли SaaS для использования в среде QS.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run sense</pre>
<h3 id="toc-hId-1941939702"><span>Dashboard:</span></h3>
<p><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bed_3-1618229582498.gif"><span class="lia-message-image-wrapper lia-message-image-actions-narrow lia-message-image-actions-below"><img loading="lazy" decoding="async" src="https://community.qlik.com/t5/image/serverpage/image-id/52798iA7421A5218DE8F8B/image-size/large?v=v2&amp;px=999" role="button" title="Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js" alt="Building_an_advanced_Visualization_extension_using_Qlik_Nebulajs_D3js" li-image-url="https://community.qlik.com/t5/image/serverpage/image-id/52798iA7421A5218DE8F8B?v=v2" li-image-display-id="'52798iA7421A5218DE8F8B'" li-message-uid="'1798745'" li-messages-message-image="true" li-bindable="" class="lia-media-image aligncenter" li-bypass-lightbox-when-linked="true" li-use-hover-links="false" li-compiled="true" width="600" height="338" tabindex="0" /></span></span></p>
<p class="graf graf--p"><span>Если вы только начинаете работать с Nebula.js, </span><a href="https://qlik.dev/" target="_blank" rel="nofollow noopener noreferrer"><span>https://qlik.dev</span></a><span> — отличное место для ознакомления с основами и подробного изучения связанных функций.</span></p>
<p class="graf graf--p"><span>Исходный код этого проекта доступен по адресу: </span><a href="https://github.com/dipankarqlik/Nebula" target="_blank" rel="nofollow noopener noreferrer"><span>https://github.com/dipankarqlik/Nebula</span></a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/">Создание Extension с помощью qlik nebula.js и D3.js &#8211; Tutorial</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/building-advanced-visualization-extension-nebula-js-d3-js/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Создание Extensions с помощью nebula.js &#8211; пример Hello World</title>
		<link>https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/</link>
					<comments>https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Fri, 11 Feb 2022 20:33:07 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Hello World - What is Nebula.js]]></category>
		<category><![CDATA[Hello World nebula.js extension]]></category>
		<category><![CDATA[Making Sense of Qlik APIs – Nebula CLI]]></category>
		<category><![CDATA[nebula.js]]></category>
		<category><![CDATA[nebula.js extension]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<category><![CDATA[What is Nebula.js]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2684</guid>

					<description><![CDATA[<p>В этом уроке мы создадим расширение HelloWorld, используя nebula.js и Qlik Sense Desktop Видео Hello World Extension &#8211; What is Nebula.js? Making Sense of Qlik APIs – Nebula CLI Создайем расширение HelloWorld, используя nebula.js В этом руководстве вы узнаете, как создать простое расширение, отображающее таблицу с использованием nebula.js. В статье будут<a class="moretag" href="https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/">Создание Extensions с помощью nebula.js &#8211; пример Hello World</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<blockquote><p>В этом уроке мы создадим расширение HelloWorld, используя nebula.js и Qlik Sense Desktop</p></blockquote>
<h1 data-testid="docs-header">Видео</h1>
<h2 data-testid="docs-header">Hello World Extension &#8211; What is Nebula.js?</h2>
<p><iframe title="Hello World - What is Nebula.js?" width="750" height="563" src="https://www.youtube.com/embed/MxQVuorsjVc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h2>Making Sense of Qlik APIs – Nebula CLI</h2>
<p><iframe title="Making Sense of Qlik APIs – Nebula CLI" width="750" height="563" src="https://www.youtube.com/embed/oF68t8DPpsE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h1 data-testid="docs-header" class=""><span>Создайем расширение HelloWorld, используя nebula.js</span></h1>
<p><span>В этом руководстве вы узнаете, как создать простое расширение, отображающее таблицу с использованием </span><code class="language-text">nebula.js</code><span>.</span></p>
<p><span>В статье будут пройдены следующие шаги:</span></p>
<ol>
<li><span>Создание проекта</span></li>
<li><span>Настройка структуры данных</span></li>
<li><span>Данные рендеринга</span></li>
<li><span>Выбор данных</span></li>
<li><span>Создание и загрузка расширения</span></li>
</ol>
<blockquote><p><strong><span>Примечание.</span></strong><span> Чтобы загрузить расширение в Qlik Sense SaaS, вам необходим доступ к тенанту Qlik Sense SaaS и либо права администратора тенанта, либо средства для получения ключа API, сгенерированного для вас. Вы можете </span><a href="https://www.qlik.com/us/trial/qlik-sense-business"><span>подписаться на бесплатную пробную версию</span></a><span> , если у вас нет существующего клиента.</span></p></blockquote>
<h2 id="requirements">Требования</h2>
<p><span>Этот учебник требует, чтобы у вас было следующее:</span></p>
<ul>
<li><a href="https://nodejs.org/en/download/"><span>Node.js</span></a><span> (версия 10 или новее)</span></li>
<li><span>Терминал (например , </span><a href="https://gitforwindows.org/"><span>Git Bash для Windows</span></a><span> или Terminal.app для Mac)</span></li>
<li><span>Современный веб-браузер, например Google Chrome</span></li>
<li><span>Текстовый редактор или IDE по вашему выбору, например Visual Studio Code.</span></li>
<li><span>Существующая </span><a href="https://qlik.dev/basics/authentication-options#web-integrations"><span>веб-интеграция</span></a><span> или возможность ее создания. Или клиент Qlik Sense Desktop.</span></li>
</ul>
<h2 id="create-a-project">Создание проекта</h2>
<p><span>Самый быстрый способ начать работу — использовать </span><code class="language-text">nebula.js</code> <span>интерфейс командной строки:</span></p>
<div class="gatsby-highlight" data-language="bash">
<pre class="language-bash" tabindex="0"><code class="language-bash">npx @nebula.js/cli create hello --picasso none</code></pre>
</div>
<p><span>Параметр </span><code class="language-text">--picasso none</code> <span>указывает команде не создавать шаблон визуализации Пикассо, другие параметры &#8211; </span><code class="language-text">minimal</code><span>и </span><code class="language-text">barchart</code><span>.</span></p>
<p><span>Команда формирует проект в папке </span><code class="language-text">/hello</code> <span>со следующей структурой:</span></p>
<ul>
<li><code class="language-text">/src</code>
<ul>
<li><code class="language-text">index.js</code> <span>&#8211; Основная точка входа этой визуализации</span></li>
<li><code class="language-text">object-properties.js</code> <span>&#8211; Свойства объекта, хранящиеся в приложении</span></li>
<li><code class="language-text">data.js</code> <span>&#8211; Конфигурация данных</span></li>
</ul>
</li>
<li><code class="language-text">/test</code> <span>&#8211; Интеграционные тесты</span></li>
<li><code class="language-text">package.json</code></li>
</ul>
<p><span>Папка содержит несколько дополнительных точечных файлов, обеспечивающих проверку и форматирование кода.</span></p>
<h3 id="start-the-development-server">Запуск сервера разработки</h3>
<p><span>Запустите сервер разработки с помощью команды:</span></p>
<div class="gatsby-highlight" data-language="bash">
<pre class="language-bash" tabindex="0"><code class="language-bash">cd hello<span></span>
npm run start</code></pre>
</div>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension.jpg" alt="" class="aligncenter size-full wp-image-2771" width="1348" height="138" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension.jpg 1348w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension-300x31.jpg 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension-1024x105.jpg 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_development_server_qlik_sense_extension-768x79.jpg 768w" sizes="(max-width: 1348px) 100vw, 1348px" /></a></p>
<p><span>Команда запускает локальный сервер разработки и открывает </span><a href="http://localhost:8080/"><span>http://localhost:8080</span></a><span> в вашем браузере.</span></p>
<p><img decoding="async" src="https://qlik.dev/static/hub-connect-f5711011cb5bed761c101f5dce82214a.png" alt="Подключиться к движку" /></p>
<p><span>Сервер разработки должен подключаться к ассоциативному движку Qlik, работающему в любом развертывании Qlik. Введите URL-адрес WebSocket, соответствующий используемому вами продукту Qlik.</span></p>
<p><strong>Чтобы открыть WebSocket для движка, используйте один из следующих URI:</strong></p>
<ul>
<li><strong>Qlik Sense Enterprise:</strong> <code>wss://server.domain.com:4747/</code> или <code>wss://server.domain.com[/virtual proxy]/</code></li>
<li><strong>Qlik Sense Desktop:</strong> <code>ws://localhost:4848/</code></li>
</ul>
<p><span>Затем выберите приложение для подключения.</span></p>
<p><img decoding="async" src="https://qlik.dev/static/hub-app-275f908203b91a5417c556d5d3b50eeb.png" alt="Подключиться к приложению" /></p>
<p><span>Затем вы будете перенаправлены в основной пользовательский интерфейс разработчика, где сможете увидеть отрендеренную визуализацию:</span></p>
<p><img decoding="async" src="https://qlik.dev/static/hub-dev-21416471f69a21ff6e9ed3f07ac66810.png" alt="Сервер разработки" /></p>
<p><span>Любые обновления </span><code class="language-text">/src/index.js</code><span>, влияющие на вывод, автоматически вызывают обновление визуализации, и вы сразу же видите изменения.</span></p>
<h2 id="configure-data-structure">Настроить структуру данных</h2>
<p><span>Простое </span><code class="language-text">Hello</code><span>сообщение на самом деле не так уж полезно, пора добавить некоторые данные.</span></p>
<p><span>Добавьте </span><code class="language-text">qHyperCubeDef</code> <span>определение в </span><code class="language-text">object-properties.js</code><span>:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> properties <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">qHyperCubeDef</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">qInitialDataFetch</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token literal-property property">qWidth</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token literal-property property">qHeight</span><span class="token operator">:</span> <span class="token number">10</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
</div>
<p><span>Затем добавьте </span><code class="language-text">/qHyperCubeDef</code> <span>в качестве цели данных в </span><code class="language-text">data.js</code><span>:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">targets</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'/qHyperCubeDef'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
</div>
<p><span>После этих изменений у вас должна появиться возможность добавлять данные на панели свойств справа:</span></p>
<p><img decoding="async" src="https://qlik.dev/static/tutorial-data-targets-f5fb58c59929b6886ccb25aeac35806b.png" alt="Цели данных" /></p>
<p><span>Добавьте измерение, нажав </span><strong><span>Add dimension</span></strong><span> и выбрав значение в появившемся меню. Затем проделайте то же самое с мерой &#8211; <strong>Add measures</strong>.</span></p>
<h2 id="render-data">Данные рендеринга</h2>
<p><span>Чтобы отобразить данные, вам сначала нужно получить к ним доступ через </span><code class="language-text">useLayout</code> <span>хук:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> useLayout<span class="token punctuation">,</span> useElement<span class="token punctuation">,</span> useEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nebula.js/stardust'</span><span class="token punctuation">;</span><span></span>
<span></span>
<span class="token comment">// ...</span>
<span class="token function">component</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">useLayout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
</div>
<p><span>Затем вы можете </span><code class="language-text">useLayout</code> <span>в сочетании с </span><code class="language-text">useEffect</code> <span>отображать заголовки и строки данных в </span><code class="language-text">qHyperCube</code><span>:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">component</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> element <span class="token operator">=</span> <span class="token function">useElement</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> layout <span class="token operator">=</span> <span class="token function">useLayout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span></span>
<span></span>
  <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>layout<span class="token punctuation">.</span>qSelectionInfo<span class="token punctuation">.</span>qInSelections<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// skip rendering when in selection mode</span>
      <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> hc <span class="token operator">=</span> layout<span class="token punctuation">.</span>qHyperCube<span class="token punctuation">;</span><span></span>
<span></span>
    <span class="token comment">// headers</span>
    <span class="token keyword">const</span> columns <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>hc<span class="token punctuation">.</span>qDimensionInfo<span class="token punctuation">,</span> <span class="token operator">...</span>hc<span class="token punctuation">.</span>qMeasureInfo<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">f</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> f<span class="token punctuation">.</span>qFallbackTitle<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> header <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;thead&gt;&lt;tr&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>columns<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token template-punctuation string">`</span><span class="token string">&lt;th&gt;</span><span class="token interpolation-punctuation punctuation">${</span>c<span class="token interpolation-punctuation punctuation">}</span><span class="token string">&lt;/th&gt;</span><span class="token template-punctuation string">`</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/tr&gt;&lt;/thead&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><span></span>
<span></span>
    <span class="token comment">// rows</span>
    <span class="token keyword">const</span> rows <span class="token operator">=</span> hc<span class="token punctuation">.</span>qDataPages<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>qMatrix
      <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">row</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;tr&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>row<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cell</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token template-punctuation string">`</span><span class="token string">&lt;td&gt;</span><span class="token interpolation-punctuation punctuation">${</span>cell<span class="token punctuation">.</span>qText<span class="token interpolation-punctuation punctuation">}</span><span class="token string">&lt;/td&gt;</span><span class="token template-punctuation string">`</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/tr&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span></span>
<span></span>
    <span class="token comment">// table</span>
    <span class="token keyword">const</span> table <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;table&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>header<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;tbody&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>rows<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/tbody&gt;&lt;/table&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><span></span>
<span></span>
    <span class="token comment">// output</span>
    element<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> table<span class="token punctuation">;</span><span></span>
<span></span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>element<span class="token punctuation">,</span> layout<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
</div>
<h3>Выберите данные</h3>
<p><span>Перед выбором данных необходимо добавить метаданные в каждую строку, чтобы значения можно было идентифицировать для выбора:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// rows</span>
<span class="token keyword">const</span> rows <span class="token operator">=</span> hc<span class="token punctuation">.</span>qDataPages<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>qMatrix
  <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>
    <span class="token punctuation">(</span><span class="token parameter">row<span class="token punctuation">,</span> rowIdx</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
      <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;tr data-row="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>rowIdx<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"&gt;
        </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>row<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cell</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token template-punctuation string">`</span><span class="token string">&lt;td&gt;</span><span class="token interpolation-punctuation punctuation">${</span>cell<span class="token punctuation">.</span>qText<span class="token interpolation-punctuation punctuation">}</span><span class="token string">&lt;/td&gt;</span><span class="token template-punctuation string">`</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
      &lt;/tr&gt;</span><span class="token template-punctuation string">`</span></span>
  <span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
</div>
<p><span>Затем добавьте </span><code class="language-text">'click'</code><span>обработчик событий, </span><code class="language-text">element</code><span>который выполняет следующие действия:</span></p>
<ul>
<li><span>Проверяет, что выбранный элемент является </span><code class="language-text">td</code></li>
<li><span>Начинает выбор </span><code class="language-text">/qHyperCubeDef</code><span>, если он еще не активирован</span></li>
<li><span>Извлекает </span><code class="language-text">data-row</code> <span>индекс из</span> <code class="language-text">tr</code></li>
<li><span>Обновления </span><code class="language-text">selectedRows</code> <span>по клику</span> <code class="language-text">data-row</code></li>
</ul>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> element <span class="token operator">=</span> <span class="token function">useElement</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> selections <span class="token operator">=</span> <span class="token function">useSelections</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">[</span>selectedRows<span class="token punctuation">,</span> setSelectedRows<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span></span>
<span></span>
<span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function-variable function">listener</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>tagName <span class="token operator">===</span> <span class="token string">'TD'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>selections<span class="token punctuation">.</span><span class="token function">isActive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        selections<span class="token punctuation">.</span><span class="token function">begin</span><span class="token punctuation">(</span><span class="token string">'/qHyperCubeDef'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">const</span> row <span class="token operator">=</span> <span class="token operator">+</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>parentElement<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'data-row'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">setSelectedRows</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">prev</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>prev<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">return</span> prev<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">v</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> v <span class="token operator">!==</span> row<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token operator">...</span>prev<span class="token punctuation">,</span> row<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span><span></span>
<span></span>
  element<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span><span></span>
<span></span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    element<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>element<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
</div>
<p><span>Затем обновите стиль выбранных строк в таблице всякий раз, когда они изменяются:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>layout<span class="token punctuation">.</span>qSelectionInfo<span class="token punctuation">.</span>qInSelections<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// no need to update when not in selection mode</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  element<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'tbody tr'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">tr</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> idx <span class="token operator">=</span> <span class="token operator">+</span>tr<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'data-row'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    tr<span class="token punctuation">.</span>style<span class="token punctuation">.</span>backgroundColor <span class="token operator">=</span> selectedRows<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>idx<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">'#eee'</span> <span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>element<span class="token punctuation">,</span> selectedRows<span class="token punctuation">,</span> layout<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
</div>
<p><span>Наконец, примените выбранные значения через </span><code class="language-text">selections</code> <span>API:</span></p>
<div class="gatsby-highlight" data-language="js">
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>selections<span class="token punctuation">.</span><span class="token function">isActive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedRows<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      selections<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'selectHyperCubeCells'</span><span class="token punctuation">,</span>
        <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'/qHyperCubeDef'</span><span class="token punctuation">,</span> selectedRows<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      selections<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'resetMadeSelections'</span><span class="token punctuation">,</span>
        <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedRows<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">setSelectedRows</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>selections<span class="token punctuation">.</span><span class="token function">isActive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> selectedRows<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
</div>
<h2 id="build-and-upload-extension">Создайте и загрузите расширение</h2>
<p><span>До сих пор вы работали в локальной изолированной среде над диаграммой, которая не зависела от какого-либо конкретного продукта Qlik.</span></p>
<p><span>Команда:</span></p>
<div class="gatsby-highlight" data-language="bash">
<pre class="language-bash" tabindex="0"><code class="language-bash">npm run build</code></pre>
</div>
<p><span>генерирует пакет в </span><code class="language-text">/dist</code> <span>папку, и это все, что вам нужно для распространения диаграммы в виде пакета npm.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/npm_run_build_result.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/npm_run_build_result.jpg" alt="" class="aligncenter size-full wp-image-2773" width="619" height="167" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/npm_run_build_result.jpg 619w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/npm_run_build_result-300x81.jpg 300w" sizes="(max-width: 619px) 100vw, 619px" /></a></p>
<p><span>Однако Qlik Sense требует некоторых дополнительных файлов и немного другого формата для интерпретации диаграммы как расширения. Чтобы сгенерировать эти файлы, вы можете запустить команду:</span></p>
<div class="gatsby-highlight" data-language="bash">
<pre class="language-bash" tabindex="0"><code class="language-bash">npm run sense</code></pre>
</div>
<p><span>Команда создает все файлы в папку </span><code class="language-text">/hello-ext</code><span>, которую затем можно использовать в качестве расширения в Qlik Sense по вашему выбору.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_npm_run_sense_result.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_npm_run_sense_result.jpg" alt="" class="aligncenter size-full wp-image-2774" width="622" height="169" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_npm_run_sense_result.jpg 622w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2022/02/nebula_npm_run_sense_result-300x82.jpg 300w" sizes="(max-width: 622px) 100vw, 622px" /></a></p>
<p><span>Чтобы загрузить расширение в клиент Qlik Sense SaaS, следуйте инструкциям по </span><a href="https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Admin/mc-extensions.htm"><span>управлению расширениями</span></a><span>.</span></p>
<p><span style="color: #ff6600;"><strong>Важная и неочевидный шаг для использования собранного расширения из директории hello-ext:</strong> </span>Обязательно нужно удалить <strong>&#8220;-ext&#8221;</strong> из директории, иначе экстеншен не будет работать и появится приписка, что экстеншен не найден на сервере. Хотя в списке визуализаций в Qlik Sense он появится.</p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/">Создание Extensions с помощью nebula.js &#8211; пример Hello World</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/build-a-helloworld-extension-using-nebula-js/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>qExt v2.0 &#8211; Extension Development Environment (Qlik Sense)</title>
		<link>https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/</link>
					<comments>https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Fri, 11 Feb 2022 19:38:31 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Extension Development Environment]]></category>
		<category><![CDATA[qext]]></category>
		<category><![CDATA[qExt v2.0]]></category>
		<category><![CDATA[qext-scripts]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2672</guid>

					<description><![CDATA[<p>qExt v2.0 Tutorial qExt — это инструмент, помогающий автоматизировать создание и развертывание расширений визуализации Qlik Sense. Он может настроить вас на стандартные среды разработки расширений и более простой метод импорта скомпилированного кода на ваш сервер Qlik, чтобы вам не приходилось каждый раз вручную удалять и импортировать расширение. Посмотреть проект на GitHub Qlik<a class="moretag" href="https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/">qExt v2.0 &#8211; Extension Development Environment (Qlik Sense)</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>qExt v2.0 Tutorial</h1>
<p><span>qExt — это инструмент, помогающий автоматизировать создание и развертывание расширений визуализации Qlik Sense. Он может настроить вас на стандартные среды разработки расширений и более простой метод импорта скомпилированного кода на ваш сервер Qlik, чтобы вам не приходилось каждый раз вручную удалять и импортировать расширение.</span></p>
<p><span>Посмотреть </span><a href="https://github.com/axisgroup/qExt" target="_blank" rel="noopener"><span>проект на GitHub</span></a></p>
<h1>Qlik Sense Extensions &#8211; Расширения Qlik Sense</h1>
<h2>Анатомия Qlik Extension</h2>
<p><span>Расширения Qlik Sense позволяют создавать собственные объекты визуализации и функциональные объекты в приложении Qlik Sense. Расширения обычно используются для расширения возможностей визуализации Qlik для пользователей, использующих приложения Qlik в режиме самообслуживания. Дополнительные сведения об объектах расширения qlik sense можно найти на сайте Qlik </span><a href="https://help.qlik.com/en-US/sense-developer/April2020/Content/Sense_Helpsites/extend-qlik-sense.htm" target="_blank" rel="noopener"><span>здесь .</span></a></p>
<p><span>все файлы расширения должны храниться в одной папке проекта и, как минимум, должны включать </span><a href="https://opensrc.axisgroup.com/qext/qlik-extensions/anatomy.html#qext-file"><span>файл qext</span></a><span> и основной </span><a href="https://opensrc.axisgroup.com/qext/qlik-extensions/anatomy.html#script-file"><span>файл сценария</span></a>.</p>
<h3 id="qext-file"><span>файл qext</span></h3>
<p><span>файл qext в проекте расширения содержит объект json, который определяет метаданные объекта расширения. Имя файла — это уникальное имя, которое Qlik будет использовать для идентификации расширения, и оно должно совпадать с именем файла сценария.</span></p>
<p><span>как минимум, файл qext должен включать свойства имени и типа. (полные свойства qext можно найти </span><a href="https://help.qlik.com/en-US/sense-developer/April2020/Subsystems/Extensions/Content/Sense_Extensions/Overview/qext-file-overview.htm" target="_blank" rel="noopener"><span>здесь</span></a><span> )</span></p>
<p><span>пример файла qext:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">// hello-world.qext
{
    "name": "Hello World",
    "description": "Hello world example",
    "type": "visualization",
    "version": "1.0.0",
    "author": "john bellizzi"
}</pre>
<h3 id="script-file">Script File</h3>
<p><span>файл сценария в проекте расширения — это файл javascript, содержащий всю логику работы вашего расширения. он определяет, как данные поступают в объект, что отображается в объекте, свойства, которые можно установить для объекта, и многое другое. имя файла должно соответствовать вашему файлу qext</span></p>
<p><span>существует множество параметров конфигурации файла скрипта, и большинство из них можно найти в руководстве </span><a href="https://help.qlik.com/en-US/sense-developer/April2020/Subsystems/Extensions/Content/Sense_Extensions/extensions-introduction.htm" target="_blank" rel="noopener"><span>здесь</span></a><span> . базовый сценарий использует функцию определения для загрузки любых необходимых ресурсов и возвращает объект javascript, содержащий функции, которые контролируют данные, проходящие через расширение.</span></p>
<p><span>наиболее распространенным методом, используемым в расширении, является </span><code>paint</code><span>метод, который запускается каждый раз, когда происходит изменение состояния приложения или изменение размера, чтобы мы могли отображать обновления в контейнере объекта расширения. </span></p>
<p><span>Вот пример файла скрипта</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">define(["jquery"], function($) {
    return {
        paint: function($element, layout) {
            var $helloWorld = $(document.createElement("div"))
            $helloWorld.html('Hello World from the extension "SimpleHelloWorld"&lt;br/&gt;')
            $element.append($helloWorld)

            // layout contains the properties and calculated data of our object
            console.log(layout)
        },
    }
})</pre>
<h3>Deployment (Развертывание)</h3>
<p><span>при развертывании расширения на сервере qlik правильным методом является сжатие всего каталога расширения в zip-файл, а затем переход к разделу расширений qmc и импорт заархивированного файла расширения. если расширение с таким именем уже развернуто, его необходимо предварительно удалить с сервера с помощью кнопки удаления в разделе расширений qmc</span></p>
<p><span>в qlik sense desktop проект расширения просто необходимо поместить в папку </span><code>C:\Users\[UserName]\Documents\Qlik\Sense\Extensions\</code><span>. любые изменения в файлах расширения будут отражены в расширении при перезагрузке страницы приложения.</span></p>
<p><span>ознакомьтесь со </span><a href="https://opensrc.axisgroup.com/qext/qlik-extensions/why-qext.html"><span>следующим разделом</span></a><span> , чтобы узнать, как qExt помогает в настройке этих проектов и автоматизации развертывания.</span></p>
<h2>Для чего нужно qExt?</h2>
<p><span>Библиотека qExt была создана по трем основным причинам.</span></p>
<ol>
<li><span>обеспечить лучшую поддержку для компиляции </span><strong><span>javascript следующего поколения</span></strong><span></span></li>
<li><span>упростить процесс </span><strong><span>развертывания</span></strong><span> расширений в средах qlik sense</span></li>
<li><span>сделать инициализацию новых проектов расширений более воспроизводимой с помощью </span><strong><span>стандартных шаблонов кода</span></strong></li>
</ol>
<h3 id="next-gen-javascript"><span>JavaScript следующего поколения</span></h3>
<p><span>скрипт, который вы видели на предыдущей странице (и javascript, который вы увидите во многих расширениях qlik), написан на ванильном javascript, который обычно можно использовать в большинстве современных веб-браузеров. это отличное начало для многих, кто начинает знакомиться с javascript, но многие разработчики хотят использовать более новые версии и возможности javascript, включая импорт модулей, функции стрелок и многое другое.</span></p>
<p><span>существует множество инструментов, помогающих преобразовывать новый код javascript в обратно совместимые форматы. qExt использует babel в координации с webpack для компиляции всего кода в формат, который может использоваться современными браузерами.</span></p>
<h3 id="streamline-deployment"><span>Оптимизация развертывания</span></h3>
<p><span>в последнем разделе упоминался процесс развертывания расширений в наших средах qlik. если вы выполняете серьезную работу по разработке проекта, ручное решение по удалению и повторному импорту кода проекта каждый раз, когда вносятся изменения, вероятно, не подходит. именно поэтому мы встроили интеграцию развертывания в qExt. инструмент может установить прямое соединение с вашим сервером qlik и автоматически удалить старое расширение и развернуть обновленное за считанные секунды с помощью API qrs</span></p>
<h3 id="templates"><span>Шаблоны</span></h3>
<p><span>в примере кода с предыдущей страницы использовался только один из многих методов, доступных в расширениях qlik. обычно при разработке расширений мы используем другие объекты и методы, такие как initialProperties, шаблон, контроллер и другие, чтобы полностью использовать настройки наших расширений. мы обнаружили, что многие из них довольно часто используются в проектах, и вместо того, чтобы переопределять их каждый раз, когда мы создаем новое расширение, мы определили их в шаблонных проектах, которые можно использовать для создания новых проектов расширений из</span></p>
<h1>Введение в qExt Packages (Пакеты qExt)</h1>
<p><span>библиотека qExt на самом деле состоит из двух разных пакетов, которые помогают нам в разработке расширений.</span></p>
<ol>
<li><span>qext-scripts — инструмент, который запускается в нашей среде разработки расширений для компиляции кода и развертывания его в средах qlik sense.</span></li>
<li><span>qext — инструмент cli, который позволяет нам создать новый проект расширения с определенным шаблоном.</span></li>
</ol>
<h2 id="qext-scripts"><span>qext-скрипты</span></h2>
<p><span>если мы оглянемся назад на 3 проблемы, которые qExt решает в </span><a href="https://opensrc.axisgroup.com/qext/qlik-extensions/why-qext.html"><span>разделе почему qExt</span></a><span> , qext-скрипты в основном помогают нам решить первые 2 проблемы. он поддерживает разработку javascript следующего поколения, компилируя наш код в обратно совместимый формат, и упрощает процесс развертывания, автоматически архивируя скомпилированный код и отправляя его в среды, которые нам нужно обновить.</span></p>
<p><span>если у вас уже настроена среда разработки расширений, вы можете легко включить в нее qext-скрипты, установив ее из npm в свой проект расширения.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">$ npm install --save-dev qext-scripts</pre>
<h2 id="qext"><span>qext</span></h2>
<p><span>библиотека qext помогает нам решить третью проблему, которую мы задали в вопросе, почему именно qExt, реализуя шаблоны шаблонного кода. это в первую очередь интерфейс командной строки, который мы можем запускать глобально для создания проекта с указанным шаблоном каталога. он также автоматически установит зависимость qext-scripts</span></p>
<p><span>qext лучше всего устанавливать глобально, чтобы проекты можно было настраивать, откуда бы вы его ни вызывали.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">$ npm install -g qext</pre>
<p>&nbsp;</p>
<h1>Использование</h1>
<h2>Быстрый старт</h2>
<p>прочитайте это, чтобы быстро установить инструменты qext и настроить новый проект</p>
<h3 id="install-qext"><span>Установить QExt</span></h3>
<p><span>установить основной инструмент qext глобально</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install -g qext</pre>
<h3>Создать новое расширение</h3>
<p><span>откройте окно терминала, в котором вы хотите разместить свой проект, и используйте инструмент qext cli для создания и установки нового проекта расширения.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">qext --create-extension my-extension --install
cd my-extension</pre>
<p><em><span>по умолчанию qext установит </span><a href="https://opensrc.axisgroup.com/qext/templates/starter.html"><code>starter</code></a><span>шаблон проекта. другие шаблоны могут быть переданы через флаг шаблона. доступные шаблоны можно найти </span><a href="https://opensrc.axisgroup.com/qext/templates/"><span>здесь</span></a></em></p>
<h3>Build extension (сборка экстеншена)</h3>
<p><span>обновите исходные файлы расширения по мере необходимости, затем запустите</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run build</pre>
<p><span>проект расширения будет скомпилирован и заархивирован в выходной каталог</span></p>
<h3>Просмотр экстеншена</h3>
<p><span>запустите команду qext watch</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run watch</pre>
<p><span>любые изменения, которые вы делаете и сохраняете в своих исходных файлах, будут автоматически перестроены в ваш выходной каталог.</span></p>
<h3>Deploy extension</h3>
<p><span>добавьте следующую конфигурацию развертывания в файл qext.config.json</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">{
    ...,
    "serverDeploy": {
        "host": "hostname",
        "isSecure": true,
        "windowsAuth": true
    }
}</pre>
<p>затем запустите</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm run deploy</pre>
<p><span>окно терминала запросит ваши учетные данные. предоставить учетные данные для пользователя с доступом на запись к расширениям qlik sense, и новое расширение будет развернуто на сервере</span></p>
<p><em><span>конфигурация serverDeploy может потребовать другие параметры в зависимости от конфигурации сервера. проверьте </span><a href="https://opensrc.axisgroup.com/qext/configuration/qext-config-json.html"><span>qExt config</span></a><span> для полных настроек конфигурации</span></em></p>
<p><span>в качестве альтернативы вы можете аутентифицироваться с использованием аутентификации заголовка. проверить использование раздела </span><a href="https://opensrc.axisgroup.com/qext/usage/header-auth.html"><span>аутентификации заголовка для получения дополнительной информации</span></a></p>
<h1>Templates (Шаблоны)</h1>
<p><span>Шаблоны позволяют вам устанавливать предварительно определенные каталоги проектов и настройки при использовании инструмента qext cli. имя используемого шаблона может быть передано через </span><code>-t, --template [type]</code><span>флаг. ниже приведены шаблоны, которые вы можете выбрать</span></p>
<ul>
<li><a href="https://opensrc.axisgroup.com/qext/templates/vanilla-base.html"><span>vanilla-base</span></a><span> — самый простой проект, настроенный для проектов, которые будут написаны с использованием vanilla javascript.</span></li>
<li><a href="https://opensrc.axisgroup.com/qext/templates/base.html"><span>base</span></a><span> — базовый каталог проекта, который устанавливает минимальные исходные файлы, которые будут скомпилированы с использованием webpack и babel.</span></li>
<li><a href="https://opensrc.axisgroup.com/qext/templates/starter.html"><span>starter</span></a><span> — начальный шаблон, включая общие методы расширения и файлы конфигурации</span></li>
</ul>
<h2>Vanilla base</h2>
<p><span>базовый шаблон vanilla — это самый простой шаблон, с которого вы можете начать, используя qExt.</span></p>
<h3 id="project-directory"><span>Каталог проекта</span></h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">├── src
│ ├── qext-template.qext
│ ├── qext-template.js
├── static
├── package.json
├── qext.config.json</pre>
<h3><span>каталог src</span></h3>
<p><span>исходный каталог имеет 2 исходных файла </span><code>qext-template.qext</code><span>и </span><code>qext-template.js</code><span>. эти файлы следует переименовать в имя расширения, поскольку оно должно храниться в Qlik Sense (имя, которое вы видите при просмотре расширений в QMC).</span></p>
<h3 id="qextconfigjson"><span>qext.config.json</span></h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">{
    "extension": "qext-template",
    "output": "./dist",
    "vanilla": {
        "entry": "./src",
        "static": "./static"
    }
}</pre>
<p><span>файл конфигурации qext настроен с именем расширения, установленным на qext-template, и для сборки в ванильном режиме. </span><span>когда проект построен, файлы будут скопированы из исходного кода в выходной каталог как есть</span></p>
<h2>Base</h2>
<p><span>базовый шаблон — это базовый шаблон, используемый для компиляции исходного кода расширения с помощью babel и webpack.</span></p>
<h2 id="project-directory"><span>Каталог проекта</span></h2>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">├── src
│ ├── index.qext
│ ├── index.js
├── static
├── package.json
├── qext.config.json</pre>
<h3 id="src-directory"><span>каталог src</span></h3>
<p><span>исходный каталог имеет 2 исходных файла, </span><code>index.qext</code><span>и</span><code>index.js</code></p>
<h3 id="qextconfigjson"><span>qext.config.json</span></h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">{
    "extension": "qext-template",
    "output": "./dist",
    "compile": {
        "entry": "./src/index.js",
        "qext": "./src/index.qext",
        "static": "./static"
    }
}</pre>
<p><span>файл конфигурации qext настроен с именем расширения, установленным на qext-template, и для сборки в режиме компиляции. </span><span>когда проект будет построен, webpack скомпилирует файлы через файл compile.entry и поместит скомпилированные файлы в выходной каталог. </span><span>он также скопирует файл qext в выходной каталог</span></p>
<h2>Starter</h2>
<p><span>начальный шаблон устанавливает каталог вашего проекта с часто используемыми методами расширения</span></p>
<h3 id="project-directory"><span>Каталог проекта</span></h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">├── src
│ ├── index.qext
│ ├── index.js
│ ├── style.css
│ ├── methods
│ │ ├── controller.js
│ │ ├── definition.js
│ │ ├── index.js
│ │ ├── initial-properties.js
│ │ ├── paint.js
│ │ ├── resize.js
│ │ ├── template.html
├── static
├── package.json
├── qext.config.json</pre>
<h3 id="qextconfigjson"><span>qext.config.json</span></h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">{
    "extension": "qext-template",
    "output": "./dist",
    "compile": {
        "entry": "./src/index.js",
        "qext": "./src/index.qext",
        "static": "./static"
    }
}</pre>
<p><span>файл конфигурации qext настроен с именем расширения, установленным на qext-template, и для сборки в режиме компиляции. </span><span>когда проект будет построен, webpack скомпилирует файлы через файл compile.entry и поместит скомпилированные файлы в выходной каталог. </span><span>он также скопирует файл qext в выходной каталог</span></p>
<h3>src/index.qext</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">{
    "type": "visualization",
    "name": "qExt Template"
}</pre>
<p><span>это метаданные расширения. свойство name должно быть обновлено до имени, которое видит конечный пользователь на панели объектов расширения приложения qlik. файл будет переименован в </span><code>extension</code><span>значение свойства в</span><code>qext.config.json</code></p>
<h3>src/index.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">import { initialProperties, template, definition, controller, paint, resize } from "./methods"
import "./style.css"

window.define([], function() {
    return {
        initialProperties,
        template,
        definition,
        controller,
        paint,
        resize,
    }
})</pre>
<h3>src/style.css</h3>
<p><span>это файл css, используемый для определения любых стилей для нашего объекта расширения. обратите внимание, что все классы должны быть предварительно выбраны с именем класса объекта расширения, которое будет в формате</span><extension-name><span>-qv-объект. например, если вы хотите установить цвет фона элемента с классом </span><code>container</code><span>в вашем объекте расширения, а имя вашего расширения — </span><code>my-extension</code><span>, ваш css должен выглядеть так:</span></extension-name></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">.my-extension-qv-object .container {
    background-color: lightblue;
}</pre>
<h3>src/methods/initial-properties.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default {
    qHyperCubeDef: {
        qDimensions: [],
        qMeasures: [],
        qInitialDataFetch: [],
    },
}</pre>
<p><span>Initial-properties определяет определение свойств объекта при его первом создании пользователем qlik sense. </span><span>шаблон устанавливает свойства с помощью HyperCubeDef, но это можно изменить на другие определения qlik, такие как ListObjectDef, или пользовательские свойства</span></p>
<h3>src/methods/definition.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default {
    type: "items",
    component: "accordion",
    items: {
        settings: {
            uses: "settings",
        },
    },
}</pre>
<p><span>объект определения определяет конфигурацию панели свойств расширения. это основной способ, с помощью которого пользователь, добавляющий расширение на лист qlik, может настраивать и изменять параметры расширения, включая измерения, показатели, цвета и т. д.</span></p>
<p><span>больше о том, как настроить объект определения, можно найти </span><a href="https://help.qlik.com/en-US/sense-developer/April2020/Subsystems/Extensions/Content/Sense_Extensions/extensions-build-properties-panel.htm" target="_blank" rel="noopener"><span>здесь</span></a></p>
<h3>src/methods/paint.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default function($element, layout) {
    // ..paint code here
}</pre>
<p><span>функция рисования запускается каждый раз, когда происходит обновление макета нашего объекта расширения. это может быть вызвано изменением состояния выбора приложения qlik sense, а также изменением размера окна. это позволяет «перекрашивать» наше расширение при изменении, чтобы отображаемый вывод отображал самую последнюю версию данных, передаваемых в расширение. </span><code>$element</code><span>содержит ссылку элемента dom на объект расширения, который можно использовать для управления тем, что отображается в dom расширения, а layout содержит рассчитанный макет объекта расширения, включая любые выходные данные гиперкуба и настройки свойств.</span></p>
<h3>src/methods/resize.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default function($element, layout) {
    // ..resize code here
}</pre>
<p><span>методы изменения размера дают нам более точный контроль над тем, как наше расширение перерисовывается во время различных типов обновлений. в случаях, когда обновления, необходимые для изменения выбора и изменения размера, отличаются, мы можем разделить изменение размера на функцию метода изменения размера. важно отметить, что если возвращается метод изменения размера, в нашем </span><code>src/index.js</code><span>файле краска не будет запускаться при изменении размера окна.</span></p>
<h3>src/methods/template.html</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">&lt;div class="chart-container"&gt;qExt Template&lt;/div&gt;</pre>
<p><span>файл шаблона определяет исходный html, который отображается в нашем расширении. этот html можно увидеть в </span><code>$element</code><span>dom, который передается функциям рисования и изменения размера.</span></p>
<h3>src/methods/controller.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export default [
    "$scope",
    "$element",
    function($scope, $element) {
        // ..controller code here
    },
]</pre>
<p><span>функция контроллера запускается при инициализации расширения. </span><span>это может быть полезно для настройки начальной конфигурации и потоков для расширения, когда оно инициализируется на экране.</span></p>
<h3>src/methods/index.js</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">export { default as initialProperties } from "./initial-properties"
export { default as template } from "./template.html"
export { default as definition } from "./definition"
export { default as controller } from "./controller"
export { default as paint } from "./paint"
export { default as resize } from "./resize"</pre>
<p><span>этот файл экспортирует все модули из </span><code>methods</code><span>каталога</span></p>
<h1>Configuration</h1>
<h2>CLI Flags (<span>Флаги командной строки)</span></h2>
<table style="border-style: solid; border-color: #474747; width: 100%; height: 175px;">
<thead>
<tr style="height: 27px;">
<th style="height: 27px; width: 43.3333%;"><strong>Flag</strong></th>
<th style="height: 27px; width: 35.7333%;"><strong>Description</strong></th>
<th style="height: 27px; width: 10.9333%;"><strong>Required</strong></th>
<th style="height: 27px; width: 9.46667%;"><strong>Default</strong></th>
</tr>
</thead>
<tbody>
<tr style="height: 60px;">
<td style="height: 60px; width: 43.3333%;"><code>-c, --create-extension &lt;name&gt;</code></td>
<td style="height: 60px; width: 35.7333%;">name of extension</td>
<td style="height: 60px; width: 10.9333%;">Yes</td>
<td style="height: 60px; width: 9.46667%;">N/A</td>
</tr>
<tr style="height: 30px;">
<td style="height: 30px; width: 43.3333%;"><code>-t, --template [type]</code></td>
<td style="height: 30px; width: 35.7333%;">name template</td>
<td style="height: 30px; width: 10.9333%;">No</td>
<td style="height: 30px; width: 9.46667%;">starter</td>
</tr>
<tr style="height: 58px;">
<td style="height: 58px; width: 43.3333%;"><code>-i, --install</code></td>
<td style="height: 58px; width: 35.7333%;">if provided, will install qext-scripts</td>
<td style="height: 58px; width: 10.9333%;">No</td>
<td style="height: 58px; width: 9.46667%;">N/A</td>
</tr>
</tbody>
</table>
<h2>qext Config</h2>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
<th>type</th>
<th>required</th>
</tr>
</thead>
<tbody>
<tr>
<td>extension</td>
<td>name of extension object</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>output</td>
<td>directory where output files will be placed</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>vanilla</td>
<td>configuration of vanilla settings in running in vanilla mode</td>
<td><a href="https://opensrc.axisgroup.com/qext/configuration/qext-config-json.html#vanilla">vanilla</a></td>
<td>yes if compile not defined</td>
</tr>
<tr>
<td>compile</td>
<td>configuration of compile settings in running in compile mode</td>
<td><a href="https://opensrc.axisgroup.com/qext/configuration/qext-config-json.html#compile">compile</a></td>
<td>yes if vanilla not defined</td>
</tr>
<tr>
<td>serverDeploy</td>
<td>configuration of server deployment settings if deploying to server</td>
<td><a href="https://opensrc.axisgroup.com/qext/configuration/qext-config-json.html#serverdeploy">serverDeploy</a></td>
<td>no</td>
</tr>
<tr>
<td>desktopDeploy</td>
<td>configuration of desktop deployment settings if deploying to desktop environment</td>
<td><a href="https://opensrc.axisgroup.com/qext/configuration/qext-config-json.html#desktopdeploy">desktopDeploy</a></td>
<td>no</td>
</tr>
</tbody>
</table>
<h3 id="vanilla">vanilla</h3>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
<th>type</th>
<th>required</th>
</tr>
</thead>
<tbody>
<tr>
<td>entry</td>
<td>source directory path to be copied into output</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>static</td>
<td>static directory path to be copied into output</td>
<td>string</td>
<td>no</td>
</tr>
</tbody>
</table>
<h3 id="compile">compile</h3>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
<th>type</th>
<th>required</th>
</tr>
</thead>
<tbody>
<tr>
<td>entry</td>
<td>entry file path to build project from</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>qext</td>
<td>qext file path to be copied into output directory</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>static</td>
<td>static directory path to be copied into output</td>
<td>string</td>
<td>no</td>
</tr>
<tr>
<td>webpackComments</td>
<td>set to false to hide webpack compile comments when building</td>
<td>bool</td>
<td>no</td>
</tr>
<tr>
<td>altWebpackConfig</td>
<td>path to alternate webpack.config.js file that should override the default webpack config definition</td>
<td>string</td>
<td>no</td>
</tr>
</tbody>
</table>
<h3 id="serverdeploy">serverDeploy</h3>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
<th>type</th>
<th>required</th>
</tr>
</thead>
<tbody>
<tr>
<td>host</td>
<td>host name of qlik server</td>
<td>string</td>
<td>yes</td>
</tr>
<tr>
<td>port</td>
<td>port over which to access qlik server</td>
<td>number</td>
<td>no</td>
</tr>
<tr>
<td>prefix</td>
<td>prefix endpoint where resources are deployed. typically used with header authentication</td>
<td>string</td>
<td>no</td>
</tr>
<tr>
<td>isSecure</td>
<td>whether endpoint should be connected to over ssl</td>
<td>bool</td>
<td>no</td>
</tr>
<tr>
<td>allowSelfSignedSignature</td>
<td>allow connection to qlik server that has a self-signed certificate</td>
<td>bool</td>
<td>no</td>
</tr>
<tr>
<td>hdrAuthUser</td>
<td>name of user as defined in the header authentication virtual proxy</td>
<td>string</td>
<td>no</td>
</tr>
<tr>
<td>hdrAuthHeaderName</td>
<td>name of auth header property as defined in virtual proxy</td>
<td>string</td>
<td>no</td>
</tr>
<tr>
<td>windowsAuth</td>
<td>set to true to use windows authentication to deploy extension</td>
<td>bool</td>
<td>no</td>
</tr>
<tr>
<td>user</td>
<td>username associated with windowsAuth. if defined here, you will not be prompted for credentials in the terminal</td>
<td>string</td>
<td>no</td>
</tr>
<tr>
<td>password</td>
<td>password associated with windowsAuth. if defined here, you will not be prompted for credentials in the terminal</td>
<td>string</td>
<td>no</td>
</tr>
</tbody>
</table>
<h3 id="desktopdeploy">desktopDeploy</h3>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
<th>type</th>
<th>required</th>
</tr>
</thead>
<tbody>
<tr>
<td>destination</td>
<td>destination of built extension files.</td>
<td>string</td>
<td>yes</td>
</tr>
</tbody>
</table>
<h1>Использованные источники</h1>
<ol>
<li><a href="https://opensrc.axisgroup.com/qext/" target="_blank" rel="noopener">https://opensrc.axisgroup.com/qext/</a></li>
<li><a href="https://medium.com/@jbellizzi/new-qext-277121a59b93" target="_blank" rel="noopener">https://medium.com/@jbellizzi/new-qext-277121a59b93</a></li>
<li><a href="https://github.com/axisgroup/qExt" target="_blank" rel="noopener">https://github.com/axisgroup/qExt</a></li>
</ol>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/">qExt v2.0 &#8211; Extension Development Environment (Qlik Sense)</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/qext-extension-development-environment/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Панель свойств расширения или Properties Panel</title>
		<link>https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/</link>
					<comments>https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Fri, 12 Feb 2021 20:27:45 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[Building a properties panel]]></category>
		<category><![CDATA[Building visualization extensions]]></category>
		<category><![CDATA[custom properties]]></category>
		<category><![CDATA[extension custom properties]]></category>
		<category><![CDATA[Properties panel basics]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<category><![CDATA[Reusable Properties]]></category>
		<category><![CDATA[как задать свойства расширения]]></category>
		<category><![CDATA[Настраиваемые свойства Extension]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2580</guid>

					<description><![CDATA[<p>Создание панели свойств Qlik Sense Extension Введение в свойства расширения. Что такое панель свойств? Перед тем, как перейти к разработке Qlik Sense Visualization Extensions, необходимо понять основные принципы определения и использования свойств в Qlik Sense расширениях. Основная идея свойств состоит в том, чтобы предложить пользователям способ настройки поведения расширений визуализации таким же<a class="moretag" href="https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/">Панель свойств расширения или Properties Panel</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Создание панели свойств Qlik Sense Extension</h1>
<h2>Введение в свойства расширения. Что такое панель свойств?</h2>
<p>Перед тем, как перейти к разработке Qlik Sense Visualization Extensions, необходимо понять основные принципы определения и использования свойств в Qlik Sense расширениях.</p>
<p><span>Основная идея свойств состоит в том, чтобы предложить пользователям способ настройки поведения </span><em><span>расширений визуализации</span></em><span> таким же образом, как они будут управлять собственными объектами Qlik Sense. Поэтому разработчик расширения может использовать программный интерфейс для определения свойств, которые затем отображаются на правой панели свойств.</span></p>
<h2>Определение панели свойств</h2>
<p><span>Свойства структурированы иерархически (есть еще такой термин, как аккордеон панели свойств):</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Panel_Properties_Customization_Developing.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Panel_Properties_Customization_Developing.png" alt="" width="242" height="481" class="aligncenter size-full wp-image-2583" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Panel_Properties_Customization_Developing.png 242w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Panel_Properties_Customization_Developing-151x300.png 151w" sizes="(max-width: 242px) 100vw, 242px" /></a></p>
<p>A &#8211; Sections<br />
B &#8211; Header<br />
C &#8211; Items</p>
<p>Есть еще одна иллюстрация (с сайта <strong><a href="http://qliksite.io/tutorials/qliksense-visualization-extensions/part-01/06-Introduction-to-Using-Properties/" target="_blank" rel="noopener">http://qliksite.io/tutorials/qliksense-visualization-extensions/part-01/06-Introduction-to-Using-Properties/</a></strong>):</p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Development_Accordion_Concept.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Development_Accordion_Concept.png" alt="" width="582" height="466" class="aligncenter size-full wp-image-2584" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Development_Accordion_Concept.png 582w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Extension_Development_Accordion_Concept-300x240.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><strong>Accordion</strong><span></span></p>
<ul>
<li>Accordion<span> </span><strong>sections</strong>
<ul>
<li>Section<span> </span><strong>headers</strong>
<ul>
<li>Property<span> </span><strong>items</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<p><span>Свойства добавляются путем их </span><span class="syntax"><span>определения в</span></span><span> свойстве <code>definition</code> в файле JavaScript. </span></p>
<p><strong>Есть два способа определения свойств:</strong></p>
<ul>
<li><span>Повторное использование существующих встроенных свойств (</span>Reusing existing, built-in, properties)</li>
<li><span>Создать настраиваемые свойства (custom properties)</span></li>
</ul>
<p><strong>Reusable Properties:</strong></p>
<ul>
<li>Dimensions</li>
<li>Measures</li>
<li>Sorting</li>
<li>Appearance</li>
<li>Add-Ons</li>
</ul>
<h3><madcap:concept term="Building visualization extensions" xmlns:madcap="http://www.madcapsoftware.com/Schemas/MadCap.xsd"><span>Повторное использование существующих свойств</span></madcap:concept></h3>
<p><span>Вот некоторые типичные свойства, используемые для наиболее распространенных встроенных визуализаций </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span>:</span></p>
<ul>
<li><span>Все собственные визуализации имеют раздел </span><span class="ui_item"><span>Внешний вид</span></span><span> с заголовком раздела </span><span class="ui_item"><span>Общие</span></span><span> .</span></li>
<li><span>Большинство встроенных визуализаций позволяют определять </span><span class="ui_item"><span>измерения</span></span><span> и </span><span class="ui_item"><span>меры</span></span><span> .</span></li>
</ul>
<h4 id="anchor-1" tabindex="-1"><span>Определение свойств</span></h4>
<p><span>Если вы создаете расширение визуализации без defining a properties panel, </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span> по умолчанию будет включать раздел <strong>&#8220;</strong></span><strong><span class="ui_item">Внешний вид</span></strong><span><strong>&#8220;</strong> или <strong>&#8220;<span class="ui_item">Appearance</span> section&#8221;</strong>.</span></p>
<p><span>Тот же результат будет достигнут при повторном использовании раздела </span><span class="syntax"><span>настроек</span></span><span>, который является внутренним именем раздела Внешний вид.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [
        'jquery'
    ],
    function ( $ ) {
        'use strict';

        return {

            // Define what the properties panel looks like
            definition: {
                type: "items",
                component: "accordion",
                items: {
                    appearance: {
                        uses: "settings",
                    }
                }
            },
            // Paint/Rendering logic
            paint: function ( $element /*, layout*/ ) {

                $element.empty();
                var $msg = $( document.createElement( 'div' ) );
                $msg.html( 'Just demonstrating default behavior of the property panel"' );
                $element.append( $msg );

            }
        };

    } );</pre>
<p><span>На основе приведенного выше кода вы теперь можете расширить <code><span class="syntax">definition</span></code> </span><span>для повторного использования других встроенных разделов. В этом примере мы также повторно будем использовать разделы измерений, мер и сортировки.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Define what the properties panel looks like
definition: {
    type: "items",
    component: "accordion",
    items: {
        dimensions: {
            uses: "dimensions"
        },
        measures: {
            uses: "measures"
        },
        sorting: {
            uses: "sorting"
        },
        appearance: {
            uses: "settings",
        }
    }
},</pre>
<p>В результате мы получим следующую панель свойств:</p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript.jpg" alt="" width="324" height="368" class="aligncenter size-full wp-image-2585" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript.jpg 324w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript-264x300.jpg 264w" sizes="(max-width: 324px) 100vw, 324px" /></a></p>
<h4 id="anchor-2" tabindex="-1"><span>Ссылка на свойства</span></h4>
<p><span>Параметр макета (layout) передается в метод paint, и он включает текущую область расширения визуализации вместе с определенными свойствами.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Paint/Rendering logic
paint: function ( $element, layout ) {

    console.info('paint &gt;&gt; layout &gt;&gt; ', layout);

}</pre>
<p><span>Если вы посмотрите на вывод консоли, например, в Chrome DevTools, вы найдете то, что ищете.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/chrome_devtools_properties_console_log_print.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/chrome_devtools_properties_console_log_print.png" alt="" width="953" height="253" class="aligncenter size-full wp-image-2586" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/chrome_devtools_properties_console_log_print.png 953w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/chrome_devtools_properties_console_log_print-300x80.png 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/chrome_devtools_properties_console_log_print-768x204.png 768w" sizes="(max-width: 953px) 100vw, 953px" /></a></p>
<p><span>С этой информацией теперь вы можете начать выводить некоторые значения в метод <code>paint</code></span><span>.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Paint/Rendering logic
paint: function ( $element, layout ) {

    //output values from the property panel
    $element.empty();
    
    //output container
    var $msg = $( document.createElement( 'div' ) );
    
    //variable holding the output
    var html = '&lt;b&gt;Property values:&lt;/b&gt;&lt;br/&gt;';
    html += 'Title: ' + layout.title + '&lt;br/&gt;';
    html += 'Subtitle: ' + layout.subtitle + '&lt;br/&gt;';
    
    //assign the variable to the output container
    $msg.html( html );
    
    //add the output container to the current element
    $element.append( $msg );

}</pre>
<p>Получим следующий результат:</p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript_example2.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript_example2.jpg" alt="" width="549" height="495" class="aligncenter size-full wp-image-2587" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript_example2.jpg 549w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Qlik_Sense_Custom_Properties_result_of_javascript_example2-300x270.jpg 300w" sizes="(max-width: 549px) 100vw, 549px" /></a></p>
<h3 id="anchor-3" tabindex="-1"><span>Собственные свойства, которые можно использовать повторно</span></h3>
<p><span>В этом разделе перечислены собственные свойства </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense,</span></span><span> которые можно повторно использовать в определении панели свойств.</span></p>
<h4>Dimensions</h4>
<p><span>Когда вы повторно используете раздел</span> Dimensions section, <span>он включает в себя следующее</span>:</p>
<ul>
<li>Add dimension</li>
</ul>
<p><span>После добавления измерения у вас есть следующие свойства по умолчанию</span>:</p>
<ul>
<li>Field</li>
<li>Label</li>
<li>Show null values</li>
<li>Limitation</li>
<li>Show others</li>
<li>Others label</li>
</ul>
<p><strong>Пример: Add Dimensions section</strong></p>
<p><span>В приведенном ниже примере показано, как добавить раздел &#8220;<strong>Dimensions</strong>&#8221; на панель свойств вашего расширения визуализации. </span><span>Обратите внимание, что определено, что расширение визуализации должно иметь ровно одно измерение.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [], function ( ) {
    
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: {
                uses: "dimensions",
                min: 1,
                max: 1
            }
        }
    };
 
});</pre>
<h4>Measures</h4>
<p><span>Когда вы повторно используете раздел &#8220;Меры&#8221;, он включает в себя следующее</span>:</p>
<ul>
<li>Add measure</li>
</ul>
<p><span>Когда вы добавили меру, у вас появятся следующие свойства по умолчанию</span>:</p>
<ul>
<li>Label</li>
<li>Expression</li>
<li>Number formatting</li>
</ul>
<p class="example" data-mc-autonum="Example: " data-mc-conditions=""><strong><span class="autonumber">Пример: </span>Add Measures section</strong></p>
<p data-mc-conditions=""><span>В этом примере показано, как мы теперь добавили определение раздела «Меры» на панель свойств нашего расширения визуализации. </span><span>Обратите внимание, что здесь мы также определяем, что расширение визуализации должно иметь ровно одну меру.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">define( [], function ( ) {
    
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: {
                uses: "dimensions",
                min: 1,
                max: 1
            },
            measures: {
                uses: "measures",
                min: 1,
                max: 1
            }
        }
    };
 
});</pre>
<h4>Sorting</h4>
<p><span>Когда вы повторно используете раздел сортировки, он включает в себя следующее</span>:</p>
<ul>
<li>Labels of the dimension and measure<span>, используемые в расширении визуализации</span></li>
<li>Sorting option (Auto or Custom)<span>Опция Custom включает в себя следующее:</span>
<ul>
<li>Sort by expression (only available for dimensions)</li>
<li>Sort numerically</li>
<li>Sort alphabetically</li>
<li>Sort by frequency (only available for filter panes)</li>
</ul>
</li>
</ul>
<p class="example" data-mc-autonum="Example: " data-mc-conditions=""><strong><span class="autonumber">Пример: </span>Add Sorting section</strong></p>
<p data-mc-conditions=""><span>В этом примере показано, как мы теперь добавили определение раздела «Сортировка» на панель свойств вашего расширения визуализации. Мы не вносили никаких изменений в раздел «Сортировка», предварительно определенный </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span>.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">define( [], function ( ) {
    
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: {
                uses: "dimensions",
                min: 1,
                max: 1
            },
            measures: {
                uses: "measures",
                min: 1,
                max: 1
            },
            sorting: {
                uses: "sorting"
            }
        }
    };
 
});</pre>
<h4>Add-ons (Надстройки)</h4>
<p><span>Когда вы повторно используете раздел надстроек (Add-ons), он включает в себя следующее:</span></p>
<ul>
<li>Data handling</li>
<li>Reference lines</li>
</ul>
<p class="example" data-mc-autonum="Example: " data-mc-conditions=""><strong><span class="autonumber">Пример: </span>Add Add-ons section</strong></p>
<p data-mc-conditions=""><span>В этом примере показано, как мы добавляем определение раздела «Надстройки» на панель свойств вашего расширения визуализации. Мы не вносили никаких изменений в раздел «Надстройки», предварительно определенный </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span>.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [], function ( ) {
    
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: {
                uses: "dimensions",
                min: 1,
                max: 1
            },
            measures: {
                uses: "measures",
                min: 1,
                max: 1
            },
            sorting: {
                uses: "sorting"
            },
            addons: {
                uses: "addons"
            }
        }
    };
 
});</pre>
<h4>Appearance (<span class="syntax">settings</span>) &#8211; <span>Внешний вид</span></h4>
<blockquote><p><span>Appearance section называется </span><code><span class="syntax">settings</span></code><span> в коде.</span></p></blockquote>
<p><span>Когда вы повторно используете раздел «Внешний вид», он включает в себя следующее:</span></p>
<ul>
<li>General &#8211; <span>Общий</span><span>Включает настройки для заголовка, подзаголовка и сноски</span></li>
<li>Presentation &#8211; <span>Презентация</span> (<span>доступна только для визуализаций с более чем одним измерением</span>)</li>
<li>Colors and legend &#8211; <span>Цвета и легенда</span> (<span>доступно только в некоторых типах визуализаций</span>)</li>
<li>X-axis &#8211; <span>Ось X</span> (<span>доступна только в некоторых типах визуализаций</span>)</li>
<li>Y-axis &#8211; <span>Ось Y</span> (<span>доступна только в некоторых типах визуализаций</span>)</li>
</ul>
<p class="example" data-mc-autonum="Example: " data-mc-conditions=""><strong><span class="autonumber">Пример: </span>Add Appearance section</strong></p>
<p data-mc-conditions=""><span class="CommonComponentsQlik Sense"><span>В этом примере показано, как мы добавляем определение раздела «Внешний вид» на панель свойств вашего расширения визуализации. Мы не вносили никаких изменений в раздел «Надстройки», предварительно определенный </span><span>Qlik Sense</span><span>.</span></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [], function ( ) {
    
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: {
                uses: "dimensions",
                min: 1,
                max: 1
            },
            measures: {
                uses: "measures",
                min: 1,
                max: 1
            },
            sorting: {
                uses: "sorting"
            },
            addons: {
                uses: "addons"
            },
            settings: {
                uses: "settings"
            }
        }
    };
 
});</pre>
<h3><strong>Добавление дополнительных свойств (</strong><madcap:concept term="Building visualization extensions" xmlns:madcap="http://www.madcapsoftware.com/Schemas/MadCap.xsd">custom properties)</madcap:concept></h3>
<p><span>Вы можете задать <strong>custom properties</strong> для <strong>visualization extension</strong>. </span><span style="color: #339966;"><strong><span class="CommonComponentsQlik Sense">Qlik Sense</span></strong></span><span> автоматически добавляет их на панель свойств и заботится о сохранении. Затем вы найдете выбранные значения в параметре <code>layout</code>.</span></p>
<p><span>Для каждого свойства вы задаете объект JavaScript с разными полями в зависимости от того, какой тип настраиваемого свойства вы определяете.</span></p>
<p><span>Следующие компоненты пользовательского интерфейса могут использоваться для отображения ваших custom properties на панели свойств:</span></p>
<ul>
<li><strong>Button</strong> &#8211; Кнопка</li>
<li><strong>Button group</strong> &#8211; Группа кнопок</li>
<li><strong>Check box</strong> &#8211; Флажок</li>
<li><strong>Color-picker</strong> &#8211; выбор цвета</li>
<li><strong>Drop down list</strong> &#8211; Выпадающий список</li>
<li><strong>Input box / text box</strong> &#8211; <span><span>Поле ввода / текстовое поле</span></span></li>
<li><strong>Link</strong> &#8211; Ссылка</li>
<li><strong>Media</strong></li>
<li><strong>Radio button</strong> &#8211; Переключатель</li>
<li><strong>Range slider</strong> &#8211; Ползунок для выбора диапазона</li>
<li><strong>Slider</strong></li>
<li><strong>Switch</strong> &#8211; Переключатель</li>
<li><strong>Text</strong> &#8211; Текст</li>
<li><strong>Text area</strong> &#8211; Текстовая область</li>
</ul>
<p><span>Их можно сгруппировать в разделы и заголовки на панели свойств. </span><span>Каждый из них предоставляет различные параметры конфигурации для управления их поведением.</span></p>
<h4><strong>Совет: Как улучшить управляемость скриптом Javascript при разработке Extension?</strong></h4>
<p><span>Если у вас есть много разных свойств, определенных в основном файле JavaScript, вы скоро поймете, что размер файла js может стать довольно большим, и его будет трудно читать и поддерживать. Для облегчения работы с js есть разные подходы:</span></p>
<ul>
<li><span>Разделите раздел <code>definition</code> на несколько переменных JavaScript.</span></li>
<li><span>Создайте отдельный файл JavaScript для определения ваших свойств и загрузите этот файл в основной файл сценария расширения визуализации.</span></li>
</ul>
<h5><span>Определение свойств в отдельном файле JavaScript</span></h5>
<p><span>В этом разделе показано, как определить ваши свойства в отдельном файле JavaScript. </span><span>Мы начинаем со следующего кода, определенного в вашем основном файле сценария.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [
        'jquery'
    ],
    function ( $ ) {
        'use strict';

        return {

            // Define what the properties panel looks like
            definition: {
                type: "items",
                component: "accordion",
                items: {
                    dimensions: {
                        uses: "dimensions"
                    },
                    measures: {
                        uses: "measures"
                    },
                    appearance: {
                        uses: "settings",
                    }
                }
            },
            // Paint/Rendering logic
            paint: function ( $element, layout ) {
            // Main rendering logic goes here
            }
        };
    } );</pre>
<p><span>Создайте файл и назовите его <strong>properties.js</strong>. </span><span>Сохраните его в той же папке, где находятся ваши основные файлы расширения визуализации. </span><span>Новый файл <strong>properties.js</strong> должен выглядеть так:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [], function () {
    'use strict';
    // *****************************************************************************
    // Dimensions &amp; Measures
    // *****************************************************************************
    var dimensions = {
        uses: "dimensions",
        min: 0,
        max: 1
    };
    var measures = {
        uses: "measures",
        min: 0,
        max: 1
    };
    // *****************************************************************************
    // Appearance section
    // *****************************************************************************
    var appearanceSection = {
        uses: "settings"
    };
    // *****************************************************************************
    // Main properties panel definition
    // Only what is defined here is returned from properties.js
    // *****************************************************************************
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: dimensions,
            measures: measures,
            appearance: appearanceSection
        }
    };
});</pre>
<p><span>Затем вам нужно загрузить внешнее определение свойств в ваш основной файл сценария и назначить <code>definition</code></span><span>.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">define( [
        // Load properties.js using requireJS
        // Note: if you load .js files, omit the file extension
        // otherwise requireJS will not load it correctly
        './properties'
    ],
    function ( props ) {
        'use strict';
        return {
            definition: props,
            // Paint/Rendering logic
            paint: function ( $element, layout ) {
            // Main rendering logic goes here
            }
        };
    } );</pre>
<h4>Добавление basic custom property</h4>
<p><span>В этом разделе мы определим самый простой, но один из наиболее распространенных компонентов &#8211; текстовое поле.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Text box definition
var myTextBox = {
    ref: "props.myTextBox",
    label: "My text box",
    type: "string"
};</pre>
<p><span>В определении всех компонентов пользовательского интерфейса есть общие свойства, которые можно установить.</span></p>
<p><code><span class="syntax">type</span></code></p>
<p><span>Используется для всех определений типов настраиваемых свойств. Может быть </span><code><span class="API_syntax_input">string</span></code>,<span> </span><code><span class="API_syntax_input">integer</span></code>,<span> </span><code><span class="API_syntax_input">number</span></code><span> или</span><span> </span><code><span class="API_syntax_input">boolean</span></code>.</p>
<p><code><span class="syntax">ref</span></code></p>
<p><span>Имя или идентификатор, используемый для ссылки на свойство.</span></p>
<p><code><span class="syntax">label</span></code></p>
<p><span>Используется для определения метки, отображаемой на панели свойств.</span></p>
<p><code><span class="syntax">component</span></code></p>
<p><span>Используется для определения того, как свойство отображается на панели свойств.</span></p>
<div class="note"></div>
<blockquote><p><span>В приведенном выше примере <code><span class="syntax">component</span></code> </span><span>не определен. </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense </span></span><span>автоматически по умолчанию использует текстовое поле, если <code><span class="syntax">component</span></code> </span><span>не определен для элементов, <code><span class="syntax">type</span></code> </span><span class="syntax"><span>которых </span></span><span>является <code><span class="API_syntax_input">string</span></code> </span><span>или <code><span class="API_syntax_input">integer</span></code></span><span>.</span></p></blockquote>
<h4 id="anchor-3" tabindex="-1"><span>Добавление определения компонента на панель свойств</span></h4>
<p><span>В этом разделе мы добавим вновь созданное текстовое поле на основе строки в раздел Внешний вид.</span></p>
<p><strong>Перед:</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Appearance section
var appearanceSection = {
    uses: "settings"
};
// Main properties panel definition
// Only what is defined here is returned from properties.js
return {
    type: "items",
    component: "accordion",
    items: {
        appearance: appearanceSection
    }
};</pre>
<p><strong>После:</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var myTextBox = {
    ref: "props.myTextBox",
    label: "My text box",
    type: "string"
};

// Appearance section
var appearanceSection = {
    uses: "settings",
    items: {
        myTextBox: myTextBox
    }
};

// Main properties panel definition
// Only what is defined here is returned from properties.js
return {
    type: "items",
    component: "accordion",
    items: {
        appearance: appearanceSection
    }
};</pre>
<p><strong>Результат:</strong></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Adding_component_definition_to_properties_panel.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Adding_component_definition_to_properties_panel.png" alt="" width="230" height="208" class="aligncenter size-full wp-image-2592" /></a></p>
<h4 id="anchor-4" tabindex="-1"><span>Добавление собственного заголовка раздела</span></h4>
<p><span>В предыдущем примере вы добавили текстовое поле в новый раздел. В заголовке раздела автоматически повторно использовалась метка компонента. В этом разделе вы добавите собственный заголовок раздела. Для этого вы создаете новый заголовок ( </span><code><span class="syntax"><span>myNewHeader</span></span></code><span> в примере ниже) в существующем разделе «Внешний вид», а затем добавляете туда элементы:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var appearanceSection = {
    uses: "settings",
    items: {
        // Definition of the custom section header
        myNewHeader: {
            type: "items",
            label: "My header, containing text box",
            items: {
                myTextBox: myTextBox
            }
        }
    }
};

// NOTHING CHANGED HERE ...
// Return overall definition of the property accordion
return {
    type: "items",
    component: "accordion",
    items: {
        appearance: appearanceSection
    }
};</pre>
<p><strong>Результат:</strong></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Adding_component_definition_to_properties_panel_2.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/Adding_component_definition_to_properties_panel_2.png" alt="" width="230" height="205" class="aligncenter size-full wp-image-2593" /></a></p>
<h4 id="anchor-5" tabindex="-1"><span>Добавление настраиваемого раздела</span></h4>
<p><span>Вы также можете создавать совершенно новые разделы аккордеона.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">// Some components
var header1_item1 = {
    ref: "props.section1.item1",
    label: "Section 1 / Item 1",
    type: "string",
    expression: "optional"
};

...
...

// Define a custom section
myCustomSection = {
    // not necessary to define the type, component "expandable-items" will automatically
    // default to "items"
    // type: "items"
    component: "expandable-items",
    label: "My Accordion Section",
    items: {
        header1: {
            type: "items",
            label: "Header 1",
            items: {
                header1_item1: header1_item1,
                header1_item2: header1_item2
            }
        },
        header2: {
            type: "items",
            label: "Header 2",
            items: {
                header2_item1: header2_item1,
                header2_item2: header2_item2
            }
        }

    }
}</pre>
<p><span>Ключ в приведенном выше коде заключается в том, что вы добавляете <code>expandable-items</code></span><span> компонента . Остальной код работает как в предыдущих примерах.</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">return {
    type: "items",
    component: "accordion",
    items: {
        appearance: appearanceSection,
        customSection: myCustomSection
    	 }
    };
});</pre>
<p><strong>Результат:</strong></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/qlik_sense_extension_development_tutorial_custom_section.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/qlik_sense_extension_development_tutorial_custom_section.png" alt="" width="248" height="505" class="aligncenter size-full wp-image-2595" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/qlik_sense_extension_development_tutorial_custom_section.png 248w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/02/qlik_sense_extension_development_tutorial_custom_section-147x300.png 147w" sizes="(max-width: 248px) 100vw, 248px" /></a></p>
<h3 id="anchor-6" tabindex="-1"><span>Отображение и сохранение custom (настраиваемых) свойств</span></h3>
<p><span>После того как вы определили </span><strong>custom properties</strong><span>, </span><span class="CommonComponentsQlik Sense"><span>Qlik Sense</span></span><span> позаботится обо всем остальном, а именно:</span></p>
<ul>
<li><span>Отображение <strong>custom properties</strong> вместе со встроенными свойствами</span></li>
<li><span>Сохранение значений свойств. Если значение свойства изменяется, вам не нужно заботиться о сохранении, то есть о сохранении и загрузке значения свойства.</span></li>
</ul>
<h3 id="anchor-7" tabindex="-1"><span>Ссылка на значения настраиваемых свойств</span></h3>
<p><span>Ссылка на значения <strong>custom properties</strong> аналогична ссылке на значения из встроенных свойств, но с одним исключением. Используя </span><code><span class="syntax"><span>ref</span></span></code><span>, вы можете определить, как значения свойств отображаются в дереве объектов. Этот принцип применяется ко всем элементам настраиваемых свойств.</span></p>
<p class="examples" data-mc-autonum="Examples: "><strong><span class="autonumber">Примеры:</span></strong></p>
<p data-mc-autonum="Examples: "><span>Определяем текстовое поле:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var myTextBox = {
    ref: "myTextBox",
    ...
};</pre>
<p data-mc-autonum="Examples: "><span>На это значение можно ссылаться в вашем скрипте:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">console.log( layout.myTextBox );</pre>
<p data-mc-autonum="Examples: "><span>В то время как:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">var myTextBox = {
    ref: "prop.myTextBox",
    ...
};</pre>
<p data-mc-autonum="Examples: "><span>Будет вызываться с использованием:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">console.log( layout.prop.myTextBox );</pre>
<blockquote>
<p data-mc-autonum="Examples: "><span>Добавление префиксов к вашим свойствам, как показано в последних примерах выше, позволяет легко перебирать все настраиваемые свойства. Это также гарантирует отсутствие конфликтов именования стандартных объектов </span><strong><span class="CommonComponentsQlik Sense">Qlik Sense</span></strong><span>.</span></p>
</blockquote>
<h2>Определение настраиваемых свойств</h2>
<h3>Custom string properties</h3>
<p>todo</p>
<h3>Custom integer properties</h3>
<p>todo</p>
<h3>Custom number properties</h3>
<p>todo</p>
<h3>Custom array properties</h3>
<p>todo</p>
<h3>Custom button properties</h3>
<p>todo</p>
<h3>Custom button group properties</h3>
<p>todo</p>
<h3>Custom check box properties</h3>
<p>todo</p>
<h3>Custom color-picker properties</h3>
<p>todo</p>
<h3>Custom drop down list properties</h3>
<p>todo</p>
<h3>Custom link properties</h3>
<p>todo</p>
<h3>Custom media properties</h3>
<p>todo</p>
<h3>Custom radio button properties</h3>
<p>todo</p>
<h3>Custom slider properties</h3>
<p>todo</p>
<h3>Custom range-slider properties</h3>
<p>todo</p>
<h3>Custom switch properties</h3>
<p>todo</p>
<h3>Custom text properties</h3>
<p>todo</p>
<h3>Custom textarea properties</h3>
<p>todo</p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/">Панель свойств расширения или Properties Panel</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/properties-panel-basics-qlik-extension/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Extension Development &#8211; Структура объекта layout</title>
		<link>https://qliksense.ivan-shamaev.ru/layout-object-structure/</link>
					<comments>https://qliksense.ivan-shamaev.ru/layout-object-structure/#comments</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Sun, 17 Jan 2021 19:52:41 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[hypercube.qDataPages[0].qMatrix]]></category>
		<category><![CDATA[hypercube.qDimensionInfo]]></category>
		<category><![CDATA[hypercube.qMeasureInfo]]></category>
		<category><![CDATA[layout]]></category>
		<category><![CDATA[qDataPages]]></category>
		<category><![CDATA[qDimensionInfo]]></category>
		<category><![CDATA[qHyperCube]]></category>
		<category><![CDATA[qInfo]]></category>
		<category><![CDATA[qMeasureInfo]]></category>
		<category><![CDATA[qMeta]]></category>
		<category><![CDATA[Структура объекта layout]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2538</guid>

					<description><![CDATA[<p>Общая структура объекта layout disableNavMenu: false extensionMeta: {translationKey: "", icon: "table", iconChar: "puzzle", isLibraryItem: true, visible: true, …} footnote: "" qHyperCube: {qSize: {…}, qDimensionInfo: Array(1), qMeasureInfo: Array(1), qEffectiveInterColumnSortOrder: Array(2), qGrandTotalRow: Array(1), …} qInfo: {qId: "BgJXgdA", qType: "TableExtensionTest"} qMeta: {privileges: Array(4)} qSelectionInfo: {} showDetails: false showTitles: true subtitle: "" title: ""<a class="moretag" href="https://qliksense.ivan-shamaev.ru/layout-object-structure/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/layout-object-structure/">Extension Development &#8211; Структура объекта layout</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Общая структура объекта layout</h1>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">disableNavMenu: false
extensionMeta: {translationKey: "", icon: "table", iconChar: "puzzle", isLibraryItem: true, visible: true, …}
footnote: ""
qHyperCube: {qSize: {…}, qDimensionInfo: Array(1), qMeasureInfo: Array(1), qEffectiveInterColumnSortOrder: Array(2), qGrandTotalRow: Array(1), …}
qInfo: {qId: "BgJXgdA", qType: "TableExtensionTest"}
qMeta: {privileges: Array(4)}
qSelectionInfo: {}
showDetails: false
showTitles: true
subtitle: ""
title: ""
version: "1.0.0"
visualization: "TableExtensionTest"</pre>
<h2>layout -&gt; extensionMeta</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">extensionMeta:
    author: ""
    dependencies: {qlik-sense: "&gt;=3.0.x"}
    description: "Table visualization template"
    homepage: ""
    icon: "table"
    iconChar: "puzzle"
    iconPath: "M14.5,... 14.5,9 Z"
    isLibraryItem: true
    isThirdParty: true
    keywords: "qlik-sense, visualization"
    license: ""
    name: "TableExtensionTest"
    repository: ""
    template: "TableExtensionTest"
    translationKey: ""
    type: "visualization"
    version: "1.0.0"
    visible: true
footnote: ""</pre>
<h2>layout -&gt; qHyperCube</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">qHyperCube:
    qColumnOrder: Array(0)
    qDataPages: [{…}]
    qDimensionInfo: [{…}]
    qEffectiveInterColumnSortOrder: (2) [0, 1]
    qGrandTotalRow: [{…}]
    qMeasureInfo: [{…}]
    qMode: "S"
    qNoOfLeftDims: -1
    qPivotDataPages: []
    qSize: {qcx: 2, qcy: 11}
    qStackedDataPages: []
    qTreeNodesOnDim: []</pre>
<h3>layout -&gt; qHyperCube -&gt; qDataPages</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">qHyperCube:
    qDataPages: Array(1)
        0:
            qArea:
            qHeight: 11
            qLeft: 0
            qTop: 0
            qWidth: 2
    qMatrix: Array(11)
        0: Array(2)
            0: {qText: "Closed", qNum: "NaN", qElemNumber: 0, qState: "O"}
            1: {qText: "8614", qNum: 8614, qElemNumber: 0, qState: "L"}
        1: (2) [{…}, {…}]
        2: (2) [{…}, {…}]
        3: (2) [{…}, {…}]
        4: (2) [{…}, {…}]
        5: (2) [{…}, {…}]
        6: (2) [{…}, {…}]
        7: (2) [{…}, {…}]
        8: (2) [{…}, {…}]
        9: (2) [{…}, {…}]
        10: (2) [{…}, {…}]
        length: 11
    qTails: Array(1)
        0:
            qDown: 0
            qUp: 0</pre>
<h3>layout -&gt; qHyperCube -&gt; qDimensionInfo</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js">qDimensionInfo: Array(1)
    0:
        autoSort: true
        cId: "mrqmqF"
        othersLabel: "Others"
        qApprMaxGlyphCount: 35
        qAttrDimInfo: []
        qAttrExprInfo: []
        qCardinal: 11
        qCardinalities: {qCardinal: 11, qHypercubeCardinal: 11, qAllValuesCardinal: -1}
        qDimensionType: "D"
        qFallbackTitle: "Status"
        qGroupFallbackTitles: Array(1)
            0: "Status"
            length: 1
        qGroupFieldDefs: ["Status"]
        qGroupPos: 0
        qGrouping: "N"
        qIsAutoFormat: true
        qMax: "NaN"
        qMin: "NaN"
        qNumFormat: {qType: "U", qnDec: 0, qUseThou: 0}
        qSortIndicator: "A"
        qStateCounts: {qLocked: 0, qSelected: 0, qOption: 11, qDeselected: 0, qAlternative: 0, …}
        qTags: (2) ["$ascii", "$text"]
    length: 1</pre>
<h3>layout -&gt; qHyperCube -&gt; qMeasureInfo</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js">qMeasureInfo: Array(1)
    0:
        autoSort: true
        cId: "dWPU"
        numFormatFromTemplate: true
        qApprMaxGlyphCount: 4
        qAttrDimInfo: []
        qAttrExprInfo: []
        qCardinal: 0
        qFallbackTitle: "Count([Case Count])"
        qIsAutoFormat: true
        qMax: 8614
        qMin: 6
        qNumFormat:
            qDec: "."
            qFmt: "###0"
            qType: "I"
            qUseThou: 1
            qnDec: 0
        qSortIndicator: "D"
        qTrendLines: []
        length: 1</pre>
<h2>layout -&gt; qInfo</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">qInfo:
    qId: "BgJXgdA"
    qType: "TableExtensionTest"</pre>
<h2>layout -&gt; qMeta</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">qMeta:
    privileges: Array(4)
        0: "read"
        1: "update"
        2: "delete"
        3: "exportdata"</pre>
<h1>Использование layout в javascript при разработке расширения</h1>
<h2>Посчитать количество строк в таблице через layout.hypercube</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="js">hypercube = layout.qHyperCube,
rowcount = hypercube.qDataPages[0].qMatrix.length,
colcount = hypercube.qDimensionInfo.length + hypercube.qMeasureInfo.length;</pre>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/layout-object-structure/">Extension Development &#8211; Структура объекта layout</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/layout-object-structure/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<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>Отладка visualization extension с помощью web developer tools</title>
		<link>https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/</link>
					<comments>https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/#respond</comments>
		
		<dc:creator><![CDATA[qliksense-expert]]></dc:creator>
		<pubDate>Fri, 15 Jan 2021 21:37:39 +0000</pubDate>
				<category><![CDATA[Уровень 2]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[extension api]]></category>
		<category><![CDATA[Qlik Sense Extension]]></category>
		<category><![CDATA[Qlik Sense Extensions]]></category>
		<category><![CDATA[Отладка QS Extension]]></category>
		<guid isPermaLink="false">https://qliksense.ivan-shamaev.ru/?p=2520</guid>

					<description><![CDATA[<p>Отладка с помощью инструментов веб-разработчика Исторически сложилось так, что разработчики JavaScript часто использовали alert() для целей отладки, но это может восприниматься как раздражающее, поэтому вместо этого рекомендуется использовать console.log() для отправки данных в консоль браузера. Инструменты веб-разработчика Большинство веб-браузеров предлагают какой-то инструмент разработчика, но в этом разделе вы познакомитесь с некоторыми основными концепциями, основанными на Google<a class="moretag" href="https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/"> Читать дальше&#8230;</a></p>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/">Отладка visualization extension с помощью web developer tools</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1><madcap:concept term="Building visualization extensions" xmlns:madcap="http://www.madcapsoftware.com/Schemas/MadCap.xsd"><span>Отладка с помощью инструментов веб-разработчика</span></madcap:concept></h1>
<p><span>Исторически сложилось так, что разработчики JavaScript часто использовали </span><code><span class="syntax"><span>alert()</span></span></code><span> для целей отладки, но это может восприниматься как раздражающее, поэтому вместо этого рекомендуется использовать </span><code><span class="syntax"><span>console.log()</span></span></code><span> для отправки данных в консоль браузера.</span></p>
<h2 id="anchor-1" tabindex="-1"><span>Инструменты веб-разработчика</span></h2>
<p><span>Большинство веб-браузеров предлагают какой-то инструмент разработчика, но в этом разделе вы познакомитесь с некоторыми основными концепциями, основанными на Google Chrome.</span></p>
<h3><span>Инструменты разработчика в вашем браузере</span></h3>
<p><span>Если вы разрабатываете расширения визуализации с помощью </span><strong><span class="CommonComponentsSenseDesktopName">Qlik Sense Desktop</span></strong><span>, вы можете использовать свой любимый браузер для целей отладки. Просто откройте </span><span class="CommonComponentsSenseDesktopName"><span>Qlik Sense Desktop</span></span><span> и оставьте его открытым. Затем откройте свой браузер и перейдите по </span><span class="path"><span>адресу <strong><a href="http://localhost:4848" target="_blank" rel="noopener">http://localhost:4848</a></strong></span></span><span>. Отобразится Hub, и оттуда вы сможете открыть инструменты разработчика браузера.</span></p>
<h3><span>Инструменты разработчика из </span><span class="CommonComponentsSenseDesktopName"><span>Qlik Sense Desktop</span></span></h3>
<p><span>Поскольку </span><span class="CommonComponentsSenseDesktopName"><span>Qlik Sense Desktop</span></span><span> использует Chromium в качестве встроенного браузера, вы можете получить доступ к Chrome DevTools из </span><span class="CommonComponentsSenseDesktopName"><span>Qlik Sense Desktop</span></span><span>. Нажмите </span><span class="ui_item_external"><span>Ctrl + Shift</span></span><span> и одновременно щелкните правой кнопкой мыши по листу, а затем выберите «</span><span class="ui_item_external"><span>Показать DevTools»</span></span><span> в диалоговом окне.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift.jpg" alt="" width="1358" height="473" class="aligncenter size-full wp-image-2523" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift.jpg 1358w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift-300x104.jpg 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift-1024x357.jpg 1024w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/qlik_sense_devtools_ctrl_shift-768x267.jpg 768w" sizes="(max-width: 1358px) 100vw, 1358px" /></a></p>
<p><strong>Откроется окно devtools</strong></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/devtools_qlik_sense_desktop_using.jpg"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/devtools_qlik_sense_desktop_using.jpg" alt="" width="938" height="774" class="aligncenter size-full wp-image-2525" srcset="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/devtools_qlik_sense_desktop_using.jpg 938w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/devtools_qlik_sense_desktop_using-300x248.jpg 300w, https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/devtools_qlik_sense_desktop_using-768x634.jpg 768w" sizes="(max-width: 938px) 100vw, 938px" /></a></p>
<h2 id="anchor-2" tabindex="-1"><span>Отладка с помощью Chrome DevTools</span></h2>
<p><span>В этом разделе выделены три области, которые имеют большое значение при разработке расширений визуализации:</span></p>
<ul>
<li><span>Использование console</span></li>
<li>Inspect elements</li>
<li>Debugging</li>
</ul>
<h3><span>Использование консоли</span></h3>
<p><span>Как упоминалось выше, удобнее использовать console в веб DevTools вместо запуска предупреждений. Для этого используйте </span><code><span class="syntax"><span>console.info</span></span></code><span> или </span><span class="syntax"><span><code>console.log</code>,</span></span><span> чтобы передать что-нибудь в консоль.</span></p>
<p class="example" data-mc-autonum="Example: "><strong><span class="autonumber">Пример: </span></strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">paint: function ( $element, layout ) {

    var err = {
        message: 'Something went wrong',
        errCode: 'bla'
    };

    console.info( 'We are re-painting the extension' );
    console.error( 'Oops, we haven an error', err );
    console.log( 'We are here' );
    console.log( 'layout', layout );

}</pre>
<p><span>Рекомендуется потратить некоторое время на изучение более сложных концепций <code>console</code>, например:</span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">console.assert();
console.group();
console.groupCollapsed();
console.groupEnd();
console.table();</pre>
<h4><span>Некоторые соображения</span></h4>
<p><span>Функции </span><strong><span class="syntax">console.<span style="color: #333333;">xyz</span>()</span></strong><span> поддерживаются не всеми веб-браузерами, поэтому рекомендуется убедиться, что в коде не осталось </span><span class="syntax"><span><strong>console.xyz()</strong>,</span></span><span> когда он попадет в рабочую среду. Для этого есть несколько стратегий:</span></p>
<ul>
<li><span>Вручную удалите все </span><strong><span class="syntax">console.xyz()</span></strong><span> из кода перед развертыванием в производственной среде.</span></li>
<li><span>Оберните все ваши вызовы в консоль, чтобы они выполнялись только в том случае, если текущий веб-браузер поддерживает консольные вызовы.</span></li>
<li><span>Используйте инструменты, например <strong>Grunt</strong> или <strong>Gulp</strong>, для создания процесса развертывания, в котором все операторы консоли удаляются автоматически.</span></li>
</ul>
<h3>Inspect elements (<span>Осмотрите элементы)</span></h3>
<p><span>Манипуляции с объектной моделью документа (DOM) можно просмотреть с помощью инспектора элементов, который встречается в большинстве инструментов веб-разработчика. В Chrome DevTools вы получаете доступ к функции проверки элементов, щелкнув увеличительное стекло на панели инструментов.</span></p>
<p><a href="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/ui_extension_inspect_element_chrome_devtools.png"><img loading="lazy" decoding="async" src="https://qliksense.ivan-shamaev.ru/wp-content/uploads/2021/01/ui_extension_inspect_element_chrome_devtools.png" alt="" width="280" height="62" class="aligncenter size-full wp-image-2521" /></a></p>
<h3><span>Отладка</span></h3>
<p><span>Вы можете использовать developer tools для отладки в реальном времени, например:</span></p>
<ul>
<li><span>Chrome DevTools</span></li>
<li><span>Firebug</span></li>
<li><span>Инструменты разработчика F12 для Internet Explorer</span></li>
</ul>
<p>Для того, чтобы запустить режим debugging в расширении, необходимо написать команду <code>debugger;</code>.</p>
<p><strong>Пример:</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="js">paint: function($element, layout) {
    
    console.log(layout);
    
    var hypercube = layout.qHyperCube;
    
    console.log(hypercube);
    
    debugger;
}</pre>
<p>Сообщение <a href="https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/">Отладка visualization extension с помощью web developer tools</a> появились сначала на <a href="https://qliksense.ivan-shamaev.ru">Qlik Sense - Обучение, учебник, онлайн курс</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://qliksense.ivan-shamaev.ru/qlik-extension-debugging-and-web-developer-tools/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 loading="lazy" 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 loading="lazy" 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>
