Configure UI help assets base url.

This commit is contained in:
Igor Kulikov 2021-10-25 16:03:14 +03:00
parent 9e9072decf
commit 641db71ce6
46 changed files with 201 additions and 58 deletions

View File

@ -96,7 +96,7 @@ public class DashboardController extends BaseController {
public static final String DASHBOARD_DEFINITION = "The Dashboard object is a heavyweight object that contains information about the dashboard (e.g. title, image, assigned customers) and also configuration JSON (e.g. layouts, widgets, entity aliases).";
public static final String HIDDEN_FOR_MOBILE = "Exclude dashboards that are hidden for mobile";
@Value("${dashboard.max_datapoints_limit}")
@Value("${ui.dashboard.max_datapoints_limit}")
private long maxDatapointsLimit;
@ApiOperation(value = "Get server time (getServerTime)",

View File

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.queue.util.TbCoreComponent;
@RestController
@TbCoreComponent
@RequestMapping("/api")
public class UiSettingsController extends BaseController {
@Value("${ui.help.base-url}")
private String helpBaseUrl;
@ApiOperation(value = "Get UI help base url (getHelpBaseUrl)",
notes = "Get UI help base url used to fetch help assets. " +
"The actual value of the base url is configurable in the system configuration file.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/uiSettings/helpBaseUrl", method = RequestMethod.GET)
@ResponseBody
public String getHelpBaseUrl() throws ThingsboardException {
return helpBaseUrl;
}
}

View File

@ -129,10 +129,16 @@ usage:
check:
cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"
# Dashboard parameters
dashboard:
# UI parameters
ui:
# Dashboard parameters
dashboard:
# Maximum allowed datapoints fetched by widgets
max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
# Help parameters
help:
# Base url for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard/master/ui-ngx/src/assets}"
database:
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records

View File

@ -70,13 +70,12 @@ frontend http-in
acl transport_http_acl path_beg /api/v1/
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$
redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
use_backend letsencrypt_http if letsencrypt_http_acl
use_backend tb-http-backend if transport_http_acl
use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets
use_backend tb-api-backend if tb_api_acl
default_backend tb-web-backend
@ -89,10 +88,9 @@ frontend https_in
acl transport_http_acl path_beg /api/v1/
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$
use_backend tb-http-backend if transport_http_acl
use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets
use_backend tb-api-backend if tb_api_acl
default_backend tb-web-backend

View File

@ -26,10 +26,6 @@ const PROXY_CONFIG = {
"target": ruleNodeUiforwardUrl,
"secure": false,
},
"/assets/help/*/rulenode/**": {
"target": ruleNodeUiforwardUrl,
"secure": false,
},
"/static/widgets": {
"target": forwardUrl,
"secure": false,

View File

@ -0,0 +1,43 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
import { Observable } from 'rxjs';
import { publishReplay, refCount } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class UiSettingsService {
private helpBaseUrlObservable: Observable<string>;
constructor(
private http: HttpClient
) { }
public getHelpBaseUrl(): Observable<string> {
if (!this.helpBaseUrlObservable) {
this.helpBaseUrlObservable = this.http.get('/api/uiSettings/helpBaseUrl', {responseType: 'text', ...defaultHttpOptions(true)}).pipe(
publishReplay(1),
refCount()
);
}
return this.helpBaseUrlObservable;
}
}

View File

@ -18,23 +18,29 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
import { catchError, mergeMap, tap } from 'rxjs/operators';
import { helpBaseUrl } from '@shared/models/constants';
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
import { helpBaseUrl as siteBaseUrl } from '@shared/models/constants';
import { UiSettingsService } from '@core/http/ui-settings.service';
const NOT_FOUND_CONTENT = '## Not found';
const localHelpBaseUrl = '/assets';
const NOT_FOUND_CONTENT: HelpData = {
content: '## Not found',
helpBaseUrl: localHelpBaseUrl
};
@Injectable({
providedIn: 'root'
})
export class HelpService {
private helpBaseUrl = helpBaseUrl;
private siteBaseUrl = siteBaseUrl;
private helpCache: {[lang: string]: {[key: string]: string}} = {};
constructor(
private translate: TranslateService,
private http: HttpClient
private http: HttpClient,
private uiSettingsService: UiSettingsService
) {}
getHelpContent(key: string): Observable<string> {
@ -70,13 +76,38 @@ export class HelpService {
}
}
private loadHelpContent(lang: string, key: string): Observable<string> {
return this.http.get(`/assets/help/${lang}/${key}.md`, {responseType: 'text'} );
private loadHelpContent(lang: string, key: string): Observable<HelpData> {
return this.uiSettingsService.getHelpBaseUrl().pipe(
mergeMap((helpBaseUrl) => {
return this.loadHelpContentFromBaseUrl(helpBaseUrl, lang, key).pipe(
catchError((e) => {
if (localHelpBaseUrl !== helpBaseUrl) {
return this.loadHelpContentFromBaseUrl(localHelpBaseUrl, lang, key);
} else {
throw e;
}
})
);
})
);
}
private processVariables(content: string): string {
const baseUrlReg = /\${baseUrl}/g;
return content.replace(baseUrlReg, this.helpBaseUrl);
private loadHelpContentFromBaseUrl(helpBaseUrl: string, lang: string, key: string): Observable<HelpData> {
return this.http.get(`${helpBaseUrl}/help/${lang}/${key}.md`, {responseType: 'text'} ).pipe(
map((content) => {
return {
content,
helpBaseUrl
};
})
);
}
private processVariables(helpData: HelpData): string {
const baseUrlReg = /\${siteBaseUrl}/g;
helpData.content = helpData.content.replace(baseUrlReg, this.siteBaseUrl);
const helpBaseUrlReg = /\${helpBaseUrl}/g;
return helpData.content.replace(helpBaseUrlReg, helpData.helpBaseUrl);
}
private processIncludes(content: string): Observable<string> {
@ -96,3 +127,8 @@ export class HelpService {
}
}
interface HelpData {
content: string;
helpBaseUrl: string;
}

View File

@ -58,11 +58,11 @@ return details;
<br>
More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/).
More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/).
You can see the real life example, where this node is used, in the next tutorial:
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
<br>
<br>

View File

@ -59,11 +59,11 @@ return details;
<br>
More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/).
More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/).
You can see the real life example, where this node is used, in the next tutorial:
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
<br>
<br>

View File

@ -61,8 +61,8 @@ return false;
You can see real life example, how to use this node in those tutorials:
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script)
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node)
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script)
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node)
<br>
<br>

View File

@ -31,7 +31,7 @@ return 'Incoming message:\n' + JSON.stringify(msg) +
You can see real life example, how to use this node in this tutorial:
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request)
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request)
<br>
<br>

View File

@ -90,7 +90,7 @@ return [];
You can see real life example, how to use this node in this tutorial:
- [Data function based on telemetry from 2 devices{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain)
- [Data function based on telemetry from 2 devices{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain)
<br>
<br>

View File

@ -52,8 +52,8 @@ return {msg: msg, metadata: metadata, msgType: newType};
You can see real life example, how to use this node in those tutorials:
- [Transform incoming telemetry{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/)
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node)
- [Transform incoming telemetry{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/)
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node)
<br>
<br>

View File

@ -20,6 +20,7 @@ A JavaScript function performing custom action.
* Display alert dialog with entity information:
```javascript
{:code-style="max-height: 300px;"}
var title;
var content;
if (entityName) {
@ -52,6 +53,7 @@ function showAlertDialog(title, content) {
* Delete device after confirmation:
```javascript
{:code-style="max-height: 300px;"}
var $injector = widgetContext.$scope.$injector;
var dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));
var deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));

View File

@ -1,6 +1,7 @@
#### HTML template of dialog to create a device or an asset
```html
{:code-style="max-height: 400px;"}
<form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup"
(ngSubmit)="save()" class="add-entity-form">
<mat-toolbar fxLayout="row" color="primary">
@ -158,3 +159,6 @@
</form>
{:copy-code}
```
<br>
<br>

View File

@ -1,6 +1,7 @@
#### Function displaying dialog to create a device or an asset
```javascript
{:code-style="max-height: 400px;"}
let $injector = widgetContext.$scope.$injector;
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
@ -130,3 +131,6 @@ function AddEntityDialogController(instance) {
}
{:copy-code}
```
<br>
<br>

View File

@ -1,6 +1,7 @@
#### HTML template of dialog to edit a device or an asset
```html
{:code-style="max-height: 400px;"}
<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
(ngSubmit)="save()" class="edit-entity-form">
<mat-toolbar fxLayout="row" color="primary">
@ -190,3 +191,6 @@
</form>
{:copy-code}
```
<br>
<br>

View File

@ -1,6 +1,7 @@
#### Function displaying dialog to edit a device or an asset
```javascript
{:code-style="max-height: 400px;"}
let $injector = widgetContext.$scope.$injector;
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
let entityService = $injector.get(widgetContext.servicesMap.get('entityService'));
@ -218,3 +219,6 @@ function EditEntityDialogController(instance) {
}
{:copy-code}
```
<br>
<br>

View File

@ -34,7 +34,7 @@ function showQrCodeDialog(title, code, format) {
{:copy-code}
```
* Parse code as a device claiming info (in this case ```{deviceName: string, secretKey: string}```)<br>and then claim device (see [Claiming devices{:target="_blank"}](${baseUrl}/docs/user-guide/claiming-devices/) for details):
* Parse code as a device claiming info (in this case ```{deviceName: string, secretKey: string}```)<br>and then claim device (see [Claiming devices{:target="_blank"}](${siteBaseUrl}/docs/user-guide/claiming-devices/) for details):
```javascript
var $scope = widgetContext.$scope;

View File

@ -127,7 +127,7 @@ self.onDataUpdated = function() {
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/alarm-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/alarm-widget-sample.png)
In this example, the **alarmSource** and **alarms** properties of <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> are assigned to **$scope** and become accessible within HTML template.

View File

@ -57,7 +57,7 @@ self.onDataUpdated = function() {
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/external-js-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/external-js-widget-sample.png)
In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section.

View File

@ -98,7 +98,7 @@ self.onDataUpdated = function() {
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/external-js-timeseries-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/external-js-timeseries-widget-sample.png)
In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section.

View File

@ -37,7 +37,7 @@ The **Widget Editor** will open, pre-populated with the content of the default *
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/latest-values-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/latest-values-widget-sample.png)
In this example, the **data** property of <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> is assigned to the **$scope** and becomes accessible within the HTML template.

View File

@ -114,7 +114,7 @@ self.onInit = function() {
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
- Click dashboard edit button on the preview section to change the size of the resulting widget. Then click dashboard apply button. The final widget should look like the image below.
![image](${baseUrl}/images/user-guide/contribution/widgets/control-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/control-widget-sample.png)
- Click the **Save** button on the **Widget Editor Toolbar** to save widget type.
@ -123,13 +123,13 @@ To test how this widget performs RPC commands, we will need to place it in a das
- Login as Tenant administrator.
- Navigate to **Devices** and create new device with some name, for ex. "My RPC Device".
- Open device details and click "Copy Access Token" button to copy device access token to clipboard.
- Download [mqtt-js-rpc-from-server.sh{:target="_blank"}](${baseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.sh) and [mqtt-js-rpc-from-server.js{:target="_blank"}](${baseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.js). Place these files in a folder.
- Download [mqtt-js-rpc-from-server.sh{:target="_blank"}](${siteBaseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.sh) and [mqtt-js-rpc-from-server.js{:target="_blank"}](${siteBaseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.js). Place these files in a folder.
Edit **mqtt-js-rpc-from-server.sh** - replace **$ACCESS_TOKEN** with your device access token from the clipboard. And install mqtt client library.
- Run **mqtt-js-rpc-from-server.sh** script. You should see a "connected" message in the console.
- Navigate to **Dashboards** and create a new dashboard with some name, for ex. "My first control dashboard". Open this dashboard.
- Click dashboard "edit" button. In the dashboard edit mode, click the "Entity aliases" button located on the dashboard toolbar.
![image](${baseUrl}/images/user-guide/contribution/widgets/dashboard-toolbar-entity-aliases.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/dashboard-toolbar-entity-aliases.png)
- Inside **Entity aliases** popup click "Add alias".
- Fill "Alias name" field, for ex. "My RPC Device Alias".
@ -137,12 +137,12 @@ To test how this widget performs RPC commands, we will need to place it in a das
- Choose "Device" in "Type" field.
- Select your device in "Entity list" field. In this example "My RPC Device".
![image](${baseUrl}/images/user-guide/contribution/widgets/add-rpc-device-alias.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/add-rpc-device-alias.png)
- Click "Add" and then "Save" in **Entity aliases**.
- Click dashboard "+" button then click "Create new widget" button.
![image](${baseUrl}/images/user-guide/contribution/widgets/dashboard-create-new-widget-button.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/dashboard-create-new-widget-button.png)
- Then select **Widget Bundle** where your RPC widget was saved. Select "Control widget" tab.
- Click your widget. In this example, "My first control widget".
@ -152,7 +152,7 @@ To test how this widget performs RPC commands, we will need to place it in a das
- Fill **RPC params** field with RPC params. For ex. "{ param1: "value1" }".
- Click **Send RPC command** button. You should see the following response in the widget.
![image](${baseUrl}/images/user-guide/contribution/widgets/control-widget-sample-response-one-way.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/control-widget-sample-response-one-way.png)
The following output should be printed in the device console:
@ -166,18 +166,18 @@ In order to test "Two way" RPC command mode, we need to change the corresponding
- Click dashboard "edit" button. In dashboard edit mode, click **Edit widget** button located in the header of Control widget.
- In the widget details, view select "Advanced" tab and uncheck "Is One Way Command" checkbox.
![image](${baseUrl}/images/user-guide/contribution/widgets/control-widget-sample-settings.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/control-widget-sample-settings.png)
- Click **Apply changes** button on the widget details header. Close details and click dashboard **Apply changes** button.
- Fill widget fields with RPC method name and params like in previous steps.
Click **Send RPC command** button. You should see the following response in the widget.
![image](${baseUrl}/images/user-guide/contribution/widgets/control-widget-sample-response-two-way.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/control-widget-sample-response-two-way.png)
- stop **mqtt-js-rpc-from-server.sh** script.
Click **Send RPC command** button. You should see the following response in the widget.
![image](${baseUrl}/images/user-guide/contribution/widgets/control-widget-sample-response-timeout.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/control-widget-sample-response-timeout.png)
In this example, **controlApi** is used to send RPC commands. Additionally, custom widget settings were introduced in order to configure RPC command mode and RPC request timeout.

View File

@ -58,7 +58,7 @@ self.onInit = function() {
- Click the **Run** button on the **Widget Editor Toolbar** to see the resulting **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/static-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/static-widget-sample.png)
This is just a static HTML widget. There is no subscription data and no special widget API was used.

View File

@ -75,7 +75,7 @@ self.onDataUpdated = function() {
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
![image](${baseUrl}/images/user-guide/contribution/widgets/timeseries-widget-sample.png)
![image](${helpBaseUrl}/help/images/widget/editor/examples/timeseries-widget-sample.png)
In this example, the <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> **datasources** and **data** properties are assigned to **$scope** and become accessible within the HTML template.

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
All widget related JavaScript code according to the [Widget API{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api).
All widget related JavaScript code according to the [Widget API{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api).
The built-in variable **self** is a reference to the widget instance.<br>
Each widget function should be defined as a property of the **self** variable.
**self** variable has property **ctx** of type [WidgetContext{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L107) - a reference to widget context that has all necessary API and data used by widget instance.
@ -129,7 +129,7 @@ Browser debugger (if enabled) will automatically pause code execution at the deb
##### Further reading
For more information read [Widgets Development Guide{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development).
For more information read [Widgets Development Guide{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development).
<br>
<br>

View File

@ -3,10 +3,10 @@
<div class="divider"></div>
<br/>
The widget subscription object is instance of [IWidgetSubscription{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/core/api/widget-api.models.ts#L264") and contains all subscription information, including current data, according to the [widget type{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#widget-types).
The widget subscription object is instance of [IWidgetSubscription{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/core/api/widget-api.models.ts#L264") and contains all subscription information, including current data, according to the [widget type{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#widget-types).
Depending on widget type, subscription object provides different data structures.
For [Latest values{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#latest-values) and [Time-series{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#time-series) widget types, it provides the following properties:
For [Latest values{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#latest-values) and [Time-series{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#time-series) widget types, it provides the following properties:
- **datasources** - array of datasources (Array<[Datasource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L279)>) used by this subscription, using the following structure:
@ -54,7 +54,7 @@ For [Latest values{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-libra
]
```
For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties:
For [Alarm widget{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties:
- **alarmSource** - ([Datasource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L279)) information about entity for which alarms are fetched, using the following structure:
@ -110,4 +110,4 @@ For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-librar
]
```
For [RPC{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#rpc-control-widget) or [Static{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#static) widget types, subscription object is optional and does not contain necessary information.
For [RPC{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#rpc-control-widget) or [Static{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#static) widget types, subscription object is optional and does not contain necessary information.

View File

@ -33,7 +33,7 @@ return '# Some title\n - Entity name: ' + data[0]['entityName'];
<ul>
<li>
Display greetings for currently logged-in user.<br>
Let's assume widget has first datasource configured using <code>Current User</code> <a target="_blank" href="${baseUrl}/docs/user-guide/ui/aliases/#single-entity">Single entity</a> alias<br>
Let's assume widget has first datasource configured using <code>Current User</code> <a target="_blank" href="${siteBaseUrl}/docs/user-guide/ui/aliases/#single-entity">Single entity</a> alias<br>
and has data keys for <code>firstName</code>, <code>lastName</code> and <code>name</code> entity fields:
</li>
</ul>

View File

@ -34,7 +34,7 @@ return data[0] ? data[0]['entityName'] : '';
<li>
Prepare QR code text to use as device claiming info (in this case <code>{deviceName: string, secretKey: string}</code>).<br>
Let's assume device has <code>claimingData</code> attribute with string JSON value containing <code>secretKey</code> field<br>
(see <a target="_blank" href="${baseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>):
(see <a target="_blank" href="${siteBaseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>):
</li>
</ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB