NodeREDGuidUKR

ДОВІДНИК З NODE-RED українською мовою

На головну Розділ
   

Template ui-template

https://dashboard.flowfuse.com/nodes/widgets/ui-template.html

Надає можливість розміщувати власний код JS і HTML (включно з будь-якими компонентами Vuetify) і CSS для відтворення на інформаційній панелі.

Властивості

image-20260104195908766

рис.1.

Prop Dynamic Description
Group   Визначає, у якій групі інформаційної панелі інтерфейсу відображатиметься цей віджет.
Scope   Вузли шаблону можна використовувати для 3 цілей/областей:
Widget (Group-Scoped): Стандартний віджет HTML/Vue, що відображається в групі на інформаційній панелі.
Widget (Page-Scoped): Віджет HTML/Vue, який відображатиметься на сторінці поза будь-якими існуючими групами. Зауважте, що ці віджети відображатимуться після будь-яких груп. Прикладом використання цього може бути, якщо ви хочете мати фіксований нижній колонтитул на певній сторінці.
Widget (UI-Scoped):Віджет HTML/Vue відображається на кожній сторінці інформаційної панелі. Найчастіше використовується в поєднанні з Teleports
CSS (All Pages): Означте власні класи/стилі CSS, які застосовуються до всієї інформаційної панелі.
CSS (Single Page): Означте власні класи/стилі CSS, які застосовуються лише до однієї сторінки вашої інформаційної панелі.
Class   Додає класи CSS до віджета
Template   Вміст віджета або CSS <style>. Якщо ви використовуєте це для CSS, вам не потрібно включати теги <style>, оскільки вони будуть додані автоматично.

Написання власного CSS

Джерело

У Dashboard 2.0 вузол ui-template дозволяє писати власний CSS для Dashboard 2.0. Сюди можна додати CSS для двох різних областей:

"Image displaying the left side with the page and group where custom CSS has been applied, and the right side showcasing the UI-template with the corresponding CSS"

рис.1с.

Щоб додати класи до свого віджета, сторінки або групи, потрібно в його конфігурації у полі Class ввести назву класу, означеного в CSS.

"Screenshot showing the class property input field"

рис.1d

Написання власних віджетів

UI Template аналізуватиме різні теги та відображатиме їх на інформаційній панелі. Доступні теги:

Робота зі змінними

Будь-які змінні, які ви хочете відобразити у своєму <template />, можна зробити одним із двох способів:

<p :class="msg.payload">Hello World</p>

або, якщо ви хочете використовувати msg.payload як частину значення, ви можете зробити це:

<!-- Change color based on msg.payload. Expects payload to be either "error", "warning" or "info" -->`
<p :class="'text-' + msg.payload">Hello World</p>
<!-- or with string literals: -->
<p :class="`text-${msg.payload}`">Hello World</p>

або навіть використовуйте msg.payload як умову:

<!-- 
  Change color based on the value of msg.payload: 
   When msg.payload equals "error", set text to the predefined `text-error` color. 
   Otherwise, set text to the predefined `text-info` color.
-->
<p :class="msg.payload === 'error' ? 'text-error' : 'text-info'">Hello World</p>
<p>Hello </p>
<p>Percentage %</p>

Вбудовані змінні

Ви маєте доступ до ряду вбудованих змінних у вузлі ui-template:

Під час доступу до змінної msg всередині тегу <script /> вам потрібно додати до назви змінної префікс this. (наприклад, this.msg.payload), щоб вона знала, що ви отримуєте доступ до компоненто-пов’язаної змінної msg.

Важлива примітка: Під час першого завантаження msg.payload може мати значення null або undefined, і спроба отримати доступ до вкладеної властивості призведе до помилки. Використання оператора додаткового ланцюжка (?.), напр. msg.payload?.nested?.property запобігає виникненню цих помилок.

Доступ до глобального/потокового контексту Node-RED

Контекстні сховища flow/global недоступні в Dashboard UI, тому найкраще використовувати вузол Change перед вузлом ui-template, щоб призначити властивість msg.< > до відповідного значення зі сховища flow. або global.:

Example of using a Change node to assign a value to

рис.2. Приклад використання вузла Change для призначення значення msg.payload.

Вбудовані функції

Ми також пропонуємо деякі допоміжні функції для інтеграції Node-RED:

Надсилання даних

Отримання даних

Існує два способи відповіді на повідомлення, отримані вашим вузлом ui-template:

Варіант 1:

У VueJS ми можемо watch змінну за будь-якими змінами та відповідно реагувати.

Як зазначено вище, ми маємо доступ до змінної msg у нашому вузлі ui-template. Ми можемо спостерігати за цією змінною для будь-яких змін і відповідно реагувати:

watch: {
    msg: function () {
        // do stuff with this.msg
        // runs onLoad and onInput
    }
}

Варто зауважити, що хоча він оновлюється, коли надходять нові повідомлення, він також оновлюється, коли віджет завантажується вперше, і у віджет завантажується останній msg.

Варіант 2:

Крім того, ми можемо додати настроюваний слухач сокетів до події msg-input:<id>. Це корисно, якщо ви хочете прослуховувати повідомлення лише під час їх отримання, а не під час першого завантаження віджета.

this.$socket.on('msg-input:' + this.id, (msg) => {
    // do stuff with msg
    // runs only when messages are received
})

Це можна додати до обробника mounted () { } віджета

Приклад (Сирий JavaScript)

Зібравши це разом, ось простий початковий віджет, який сповістить користувача, коли він натисне кнопку, і надішле повідомлення в Node-RED.

<template>
    <!-- Any HTML can go here -->
    <button class="my-class" onclick="onClick()">My Button</button>
</template>

<script>
    / Write any JavaScript here /
    // add our onClick function to the window object to make it accessible by the HTML <button>
    window.onClick = function () {
        alert('Button has been clicked')
    }

    // Use send() function to pass on data back into Node-RED:
    this.send('Component has loaded')

    // Subscribe to the incoming msg's
    this.$socket.on('msg-input:' + this.id, function(msg) {
        // do stuff with the message
        alert('message received: ' + msg.payload)
    })
</script>

<style>
    / define any styles here - supports raw CSS /
    .my-class {
        color: red;
    }
</style>

Завантаження зовнішніх залежностей

Можна завантажити зовнішні залежності у ваш вузол ui-template. Це корисно, якщо ви хочете використовувати бібліотеку, яка не входить до основних вузлів Node-RED Dashboard 2.0.

Для цього вам потрібно буде завантажити залежність у розділ <script> вашого шаблону. Наприклад, щоб завантажити бібліотеку Babylon.js, ви повинні зробити наступне:

<script src="https://cdn.babylonjs.com/babylon.js"></script>

Тоді ви можете мати інший тег <script /> у тому самому ui-template, який використовує цю бібліотеку.

Важливим застереженням є те, що, хоча це вставляється в <head /> інформаційної панелі, оскільки наші віджети завантажуються після початкового завантаження сторінки, бібліотека не завжди доступна одразу після завантаження вашого віджета та HTML .

Якщо вам потрібен доступ до бібліотеки, щойно вона стане доступною, хитрістю для цього є запустити setInterval() і спостерігати за об’єктом window, щоб бібліотека завантажувалася.

Наприклад:

<template>
    <!-- Template Content Here -->
</template>

<script src="https://cdn.babylonjs.com/babylon.js"></script>

<script>
function init () {
    alert('Babylon.js is loaded')
}

// run this code when the widget is built
let interval = setInterval(() => {
    if (window.BABYLON) {
        // call an init() to use BABYLON
        init();
        // Babylon.js is loaded, so we can now use it
        clearInterval(interval);
    }
}, 100);
</script>

Створення повних компонентів Vue

Ви можете створювати повні компоненти Vue у вузлі ui-template, використовуючи Options API VueJS. Це дозволяє створювати власну індивідуальну поведінку та дає більше контролю над інтерфейсом користувача.

Повний список властивостей VueJS Options API, які ми наразі підтримуємо:

Приклад (повний компонент Vue)

Тут ми означуємо віджет лічильника та використовуємо властивості data, watch, computed і methods Vue. Цей віджет автоматично оновлюватиме змінну formattedCount щоразу, коли змінна count змінюється, і надсилатиме повідомлення до Node-RED щоразу, коли змінна count досягне значення, кратного 5.

<template>
    <div>
        <h2>Counter - loaded: </h2>
        <p>Current Count: 8</p>
        <p class="my-class">Formatted Count: </p>
        <v-btn @click="increase()">Increment</v-btn>
    </div>
</template>

<script>
    export default {
        data() {
            // define variables available component-wide
            // (in <template> and component functions)
            return {
                loaded: false,
                count: 0
            }
        },
        watch: {
            // watch for any changes of "count"
            count: function () {
                if (this.count % 5 === 0) {
                    this.send({payload: 'Multiple of 5'})
                }
            }
        },
        computed: {
            // automatically compute this variable
            // whenever VueJS deems appropriate
            formattedCount: function () {
                return `${this.count} Apples`
            }
        },
        methods: {
            // expose a method to our <template> and Vue Application
            increase: function () {
                this.count++
            }
        },
        mounted() {
            // code here when the component is first loaded
            this.loaded = true
        },
        unmounted() {
            // code here when the component is removed from the Dashboard
            // i.e. when the user navigates away from the page
        }
    }
</script>
<style>
    / define any styles here - supports raw CSS /
    .my-class {
        color: red;
    }
</style>

Все, що повертається функцією data, автоматично стає доступним для <template>. Це означає, що ми можемо використовувати змінну count у нашому шаблоні, і вона автоматично оновлюватиметься у міру зміни змінної.

Ми також можемоwatch за будь-якою з цих змінних «даних» і відповідним чином реагувати. Наприклад, вище ми надсилаємо повідомлення до Node-RED кожного разу, коли змінна count досягає числа, кратного 5.

Ми використовуємо змінну computed, яка автоматично оновлюватиметься щоразу, коли змінна count змінюється. Це дозволяє нам відформатувати змінну count у спосіб, який буде більш корисним для відображення, не впливаючи на базову змінну count.

Телепорти

Ви можете використовувати функцію Vue’s Teleport, щоб відтворити вміст у певному місці в DOM.

Код можна записати у вузол ui-template, а область дії встановити на “group”, “page” або “UI” залежно від того, коли ви хочете, щоб цей <Teleport> був активним.

Ми пропонуємо кілька попередньо визначених місць, якими ви можете скористатися:

Page Name (#app-bar-title)

Додайте вміст ліворуч від заголовка інформаційної панелі. <Teleport> можна використовувати наступним чином:

<template>
    <Teleport v-if="mounted" to="#app-bar-title">
        <v-btn>Button 1</v-btn>
        <v-btn>Button 2</v-btn>
    </Teleport>
</template>
<script>
    export default {
        data() {
            return {
                mounted: false
            }
        },
        mounted() {
            this.mounted = true
        }
    }
</script>

Це призведе до:

Example of Teleporting content to the App Bar Title

рис.3.

Приклад телепортації вмісту до заголовка панелі програми, додавання до існуючої назви сторінки

Ми також можемо вимкнути візуалізацію назви сторінки в основних налаштуваннях інформаційної панелі, тому під час використання телепорту це буде єдиний вміст, який відображається у верхньому лівому куті.

Тут ми можемо відобразити зображення (введене через msg.payload) замість назви сторінки:

<template>
    <Teleport v-if="mounted" to="#app-bar-title">
        <img height="32px" :src="msg.payload"></img>
    </Teleport>
</template>
<script>
    export default {
        data() {
            return {
                mounted: false
            }
        },
        mounted() {
            this.mounted = true
        }
    }
</script>

Це призведе до:

Example of Teleporting content to the App Bar Title

рис.4. Приклад перенесення (teleporting) вмісту в заголовок App Bar та приховування назви сторінки.

App Bar - Actions (#app-bar-actions)

Відтворює вміст у правій частині панелі додатків на інформаційній панелі. Щоб використовувати цей телепорт, ви можете використовувати такий синтаксис:

<template>
    <Teleport v-if="mounted" to="#app-bar-actions">
        <v-btn>My Action</v-btn>
    </Teleport>
</template>
<script>
    export default {
        data() {
            return {
                mounted: false
            }
        },
        mounted() {
            this.mounted = true
        }
    }
</script>

Це призведе до:

Example of Teleporting content to the App Bar

рис.5.

Зверніть увагу на використання v-if="mounted" у тезі <Teleport />. Чомусь Vue скаржиться, коли намагається відобразити телепорт всередині нашого ui-template, до того, як компонент буде повністю змонтовано. Включення цього оператора v-if запобігає цій помилці.

URL Parameters

Vue має вбудований об’єкт this.$route, який містить детальну інформацію про активний маршрут. Це включає будь-які параметри запиту, включені в URL-адресу (наприклад, /dashboard/my-page?query=param), які можна визначити за допомогою Керування інтерфейсом користувача або під час прямого переходу на сторінку.

Приклад того, як отримати доступ до цих параметрів:

<template>
    <div>
        <p>Query Parameter: </p>
    </div>
</template>

Додаткові приклади

Будь-які компоненти Vue, які ви означуєте в ui-template, розширюють основний компонент Vue ui-template. Це включає колекцію вбудованих методів, даних і підтримуваних віджетів. Ви також можете відтворювати динамічний вміст за допомогою будь-яких виразів зв’язування даних VueJS (наприклад, v-if, v-for).

Читання Node-RED Input

Щоразу, коли ui-template отримує msg у Node-RED, це автоматично призначається змінній msg у шаблоні. Таким прикладом може бути:

<template>
    <div>
        <h2>Latest <code>msg</code> received:</h2>
        <pre></pre>
    </div>
</template>

Example of UI Template displaying the last received message

рис.6.

Надсилання повідомлень на Node-RED

Два відкритих методи, send і submit, дозволяють надсилати повідомлення з інформаційної панелі до потоку Node-RED.

Надсилання по кліку

Тут ми викликаємо це, коли хтось натискає кнопку “Send Hello World” :

<v-btn @click="send({payload: 'Hello World'})">Send Hello World</v-btn>

Надсилання при зміні

Або інший приклад, де корисне навантаження автоматично надсилається щоразу, коли змінюється v-model:

Example of UI Template using Vuetify's Rating Widget

рис.7.

<v-rating hover :length="5" :size="32" v-model="value"
    active-color="primary" @update:model-value="send({payload: value})"/>

v-model у Vue — це спосіб двостороннього прив’язування змінної до віджета. Тут ми прив’язуємо змінну value до віджета v-rating. Потім ми спостерігаємо за змінами цього значення за допомогою @update:model-value і надсилаємо змінну value до потоку Node-RED через msg.payload.

Після зміни, якщо підключено до вузла «Налагодження», ми можемо побачити наступний результат:

Example output from using Vuetify's Rating Widget

рис.8.

Віджети Vuetify

Вузол ui-template також має доступ до бібліотеки компонентів Vuetify за умовчанням. Бібліотека надає велику кількість готових віджетів, які можна використовувати на інформаційній панелі.

Вони особливо корисні, оскільки забезпечують легкий доступ до великої кількості попередньо створених віджетів, які не обов’язково включені в основні вузли Node-RED Dashboard 2.0.

Деякі приклади віджетів, які можуть бути вам корисними:

Статті та посібники

Building a Custom Video Player in Dashboard 2.0

надамо короткий огляд елементи з “компонента” Vue, які ми тут використаємо:

<template>
    <!-- Our HTML content will go here -->
</template>

<script>
export default {
  name: 'MyComponent',
  methods: {
    // JS methods we want to use across our component will go here
  },
  mounted () {
    // Code we want to run when our component is loaded will go here
  },
  unmounted () {
    // Code we want to run when our component is unloaded will go here
  }
}
</script>

<style>
    / We can define custom CSS here too /
</style>

Деякі швидкі моменти, на які слід звернути увагу:

Окрім створення компонента з нуля, ми також використаємо деякі вбудовані функції ui-template. Це будуть:

Ми збираємося почати з додавання базового відеопрогравача HTML:

<template>
    <video ref="my-video" style="width: 100%" controls @play="onPlay" @pause="onPause">
        <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">
        Your browser does not support the video tag.
    </video>
</template>

Тут варто звернути увагу на кілька важливих речей:

Визначивши тільки вище, ми отримаємо стандартний відеопрогравач:

HTML5 Video Player rendered in Dashboard

рис.9.

Тепер ми починаємо створювати наш компонент Vue. Повертаючись до нашого попереднього набору функцій, ми розглянемо їх по черзі.

  1. Передача подій на Node-RED під час відтворення/паузи

Ми можемо використовувати methods, щоб визначити наші функції onPlay і onPause, які називаються @play/@pause відповідно.

<script>
export default {
  name: 'MyVideoPlayer',
  methods: {
    capture (eventType) {
        // давайте визначимо нашу власну функцію, яку можна назвати onPlay/onPause
        // це запобігає дублюванню коду в обох методах

        // get the Video's DOM element
        const video = this.$refs['my-video']

        // send a msg to Node-RED using built-in "send" fcn
        this.send({
            // specify which action is taking place
            event: eventType,
            // use Vue's $refs to get the video's currentTime
            time: video.currentTime
        })
    },
    onPlay () {
        this.capture('play')
    },
    onPause () {
        this.capture('pause')
    }
  }
}
</script>

За допомогою цієї функції ми можемо підключити вузол ui-template до вузла debug і бачити наступне, коли відтворюємо/призупиняємо відео:

Example debug output when our custom build video player is played/paused

рис.10.

  1. Дистанційне керування відтворенням/паузою від Node-RED

Ми можемо використовувати вбудовану змінну $socket для прослуховування вхідних подій від Node-RED. Коли вузли Dashboard 2.0 отримують msg всередині Node-RED, вони надсилають подію msg-input:<node-id> клієнту Dashboard. Ми можемо прослухати цю подію, а потім викликати методи play() і pause() для елемента video, залежно від будь-яких властивостей цього повідомлення, у цьому випадку значення msg.payload.event.

<script>
export default {
  name: 'MyVideoPlayer',
  methods: {
    // ...
  },
  mounted () {
    // listen for incoming msg's from Node-RED
    // note our topic is "msg-input" + the node's unique ID
    this.$socket.on('msg-input:' + this.id, (msg) => {
        // get the Video's DOM element
        const video = this.$refs['my-video']

        // if the event is "play", call the video's play() method
        if (msg.payload?.event === 'play') {
            video.play()
        }

        // if the event is "pause", call the video's pause() method
        if (msg.payload?.event === 'pause') {
            video.pause()
        }
    })
  },
  unmounted () {
    // make sure we remove our listeners when the widget is destroyed
    this.$socket.off(`msg-input:${this.id}`)
  }
}
</script>
  1. Перехід до певної точки відео з Node-RED

За допомогою прослухувача on('msg-input') тепер ми можемо розширити наш обробник для обробки пошуку до певної точки у відео.

<script>
export default {
  name: 'MyVideoPlayer',
  methods: {
    // ...
  },
  mounted () {
    // ...
    this.$socket.on('msg-input:' + this.id, (msg) => {
        // ... other handlers

        // if the event is "seek", call the video's currentTime() method
        if (msg.payload?.event === 'seek') {
            video.currentTime = msg.payload.currentTime
        }
    })
  },
  unmounted () {
    // ...
  }
}
</script>

і разом з цим ми тепер маємо віджет Dashboard 2.0 для відображення відео, яким можна керувати з Node-RED, і реєструє деталі активності користувача назад у Node-RED.