For the UIbuilder node, where are the index source files?

Every example I import into Node-RED, I get the same UIbuilder webpage (unless the index files are edited). I just want to see another simple webpage example like Quote of the Day.

Hi Adam, thanks for using uibuilder. All of the information is in the docs and can also be seen if you click on the image button. That opens a new page with loads of detailed information including a list of all of your uibuilder endpoints with their URL's and full server filing system locations. Within the listed locations, you will find a src and dist folder, the default template files are in the src folder.

There is an advanced option to turn off the auto-copy, you can change that before your first deploy if you prefer not to have the default files.

As a quick reference, you will find the uibRoot folder at ~/.node-red/uibuilder on a standard node-red install. That also contains some master configuration files. There will then be a folder with the same name as the url specified in the url field of the node. That is the folder that contains the src and dist folders.

I get the same default UIbuilder webpage because the index files are the same. Unless I'm not understanding something, I would like to just copy/paste in the index file contents for something that works besides the default (such as the contents of Quote of the Day's index files).

Hm. Maybe it has something to do with the index.html comment:

Note that adding an appcache really speeds things up after the first load
You need to amend the appcache file to meet your needs.
Don't forget to change the appcache file if you update ANY
of the files in it otherwise the old versions will ALWAYS be used.
<html lang="en" manifest="./uibuilder.appcache">

If you use one of the examples in the library, you get some comment nodes that contain the amended HTML and JS. Just use the built-in editor, open the matching file, replace the contents and hit save.

Where are these? I searched your GitHub for "quote" and cannot find anything.

Other examples are in the WIKI. WIKI examples have the index.html and index.js files as code sections, copy the code for each separately and paste into the matching file in the editor.

It's always the default UI. When I go to the WIKI, all I see is it linking back to flows.nodered.org:

https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Simple-example

If you copy the code from that flow and paste it into an import, you will get an example flow. Take the code from the comment boxes labelled index.html, index.js and index.css and paste each in turn to the matching files using the uibuilder editor. Then deploy and load the http://localhost:1880/uib_simple page.


Here is a slightly updated version of that flow. Nothing really changed except a couple of minor tweaks to the code.

[{"id":"3a85e836.06b698","type":"comment","z":"ff1a7711.244f48","name":"uibuilder/Simple Example","info":"[Front-End](/uib_simple)\n\nThis flow gets a \"quote of the day\" from the Internet and passes it\nto uibuilder. It caches the result so that if you reload the page,\nyou get the last result back. The quote is updated every 30 minutes\nduring the day and evening.\n\n\"Simple\" refers to the front-end code. While the flow looks a little\ncomplex, it really isn't. A trigger (repeating), an Internet request,\na cache and uibuilder. The link nodes loop the control output from\nuibuilder back to the cache.\n\n## Configuration\n\nUpdate the files:\n\n* `index.html`\n* `index.js`\n* `index.css`\n\nAccording to the example(s) in the 3 other comment nodes in this example.\n\nPress the button on the trigger to start the flow.","x":230,"y":120,"wires":[]},{"id":"7cf5c3b2.96006c","type":"comment","z":"ff1a7711.244f48","name":"index.html","info":"<!doctype html><html lang=\"en\"><head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <title>uibuilder simple example</title>\n    <meta name=\"description\" content=\"Node-RED uibuilder - Simple example using VueJS\">\n\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <!-- Put your own custom styles in here -->\n    <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n</head><body>\n    <!-- The \"app\" element is where the code for dynamic updates is attached -->\n\t<div id=\"app\">\n\t    <h1>A simple uibuilder page</h1>\n\t    <p>\n\t        The elements below are dynamically updated when you send\n\t        a msg to your uibuilder node.\n\t    </p>\n\t    \n\t    <div v-if=\"msg.payload\">\n\t        <h2>Quote of the Day</h2>\n\t        <blockquote>\n\t            <i>{{ msg.payload.quote.body }}</i>\n    \t        <div>{{ msg.payload.quote.author }}</div>\n\t        </blockquote>\n\t    </div>\n\t    \n\t    <h2>The full msg object</h2>\n\t\t<code>{{ msg }}</code>\n\t\t\n\t</div>\n\n    <!-- Vendor Libraries - Load in the right order -->\n    <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n    <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>\n\n    <!-- REQUIRED: Sets up Socket listeners, the uibuilder and msg objects -->\n    <script src=\"./uibuilderfe.min.js\"></script>\n\n    <!-- Put your own custom code in here -->\n    <script src=\"./index.js\"></script>\n\n</body></html>","x":420,"y":120,"wires":[],"icon":"node-red/parser-html.svg"},{"id":"8f8bfd94.685e6","type":"comment","z":"ff1a7711.244f48","name":"index.js","info":"/* jshint browser: true, esversion: 5, asi: true */\n/*globals uibuilder, Vue */\n// @ts-nocheck\n/*\n  Copyright (c) 2020 Julian Knight (Totally Information)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n*/\n'use strict'\n\n/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */\n\nvar app1 = new Vue({\n    // The HTML element to attach to\n\tel: '#app',\n\t/** Pre-defined data\n\t *  Anything defined here can be used in the HTML\n\t *  if you update it, the HTML will automatically update\n\t */\n\tdata: {\n\t\tmsg: '[Nothing Recieved Yet]',\n\t},\n\n    // Called after the Vue app has been created. A good place to put startup code\n    created: function() {\n        // **REQUIRED** Start uibuilder comms with Node-RED\n        uibuilder.start()\n    },\n\n    // Called when Vue is fully loaded\n\tmounted: function() {\n\t\t// Keep a convenient reference to the Vue app\n\t\tvar vueApp = this\n\n        /** Triggered when the node on the Node-RED server\n         *  recieves a (non-control) msg\n         */\n\t\tuibuilder.onChange('msg', function(msg) {\n\t\t\tvueApp.msg = msg\n\t\t})\n\n\t\t// Send message back to node-red\n\t\t// uibuilder.send({payload:'some message'})\n\t},\n\t\n}) // --- End of the Vue app definition --- //\n\n// EOF","x":550,"y":120,"wires":[],"icon":"font-awesome/fa-code"},{"id":"8a35b840.a3b2d8","type":"comment","z":"ff1a7711.244f48","name":"index.css","info":"body {font-family: sans-serif; font-size: 120%;}\ndiv, p, code { margin:0.3em; padding: 0.3em;}\n\nblockquote i {\n    font-size: 1.5em; color:grey; font-style: italic;\n}","x":680,"y":120,"wires":[],"icon":"node-red/hash.svg"},{"id":"2cf3f051.3dd33","type":"debug","z":"ff1a7711.244f48","name":"simple-debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":740,"y":160,"wires":[]},{"id":"eb95241.bca25d8","type":"uibuilder","z":"ff1a7711.244f48","name":"Simple Example","topic":"","url":"uib_simple","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":530,"y":180,"wires":[["2cf3f051.3dd33"],["4e7c2860.c5c318"]]},{"id":"70cf5aa5.9b03c4","type":"inject","z":"ff1a7711.244f48","name":"","repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"","payloadType":"str","x":150,"y":180,"wires":[["dcbb878b.7de8c8"]]},{"id":"4e7c2860.c5c318","type":"debug","z":"ff1a7711.244f48","name":"uib controls","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":730,"y":200,"wires":[]},{"id":"16169222.b4d91e","type":"debug","z":"ff1a7711.244f48","name":"qod-debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":530,"y":260,"wires":[]},{"id":"dcbb878b.7de8c8","type":"http request","z":"ff1a7711.244f48","name":"Quote of the day","method":"GET","ret":"obj","paytoqs":false,"url":"https://favqs.com/api/qotd","tls":"","persist":false,"proxy":"","authType":"","x":310,"y":180,"wires":[["eb95241.bca25d8","16169222.b4d91e"]]}]

I will update the flow example in a sec. I'm also adding that example to the built in examples, that will appear in the next release of uibuilder.

From where? I copy/pasted the flow from the example Quote of the Day. It stays the same. Here is the URL from the last time:

You need to re-read the instructions. When you have imported the example flow json, you get this:

Open the index.html comment box

Copy the content, close that and open the uibuilder node

Click on the Edit Source Files button, it defaults to editing the index.html file. Paste what you just copied and press the save button then close. Repeat for the other two files.

Note that the URL is uib_simple, when you press the node-red deploy button, this url is created for you. In your case it should be at http://localhost:1880/uib_simple so open that in your browser.

You will get this:

Go back to the node-red flow and press the inject button, back at the url (without reloading the page), you get something like this:

Feel free to change the inject node so that it repeats every minute.

1 Like

Thank you for taking the time to walk me through it. It doesn't seem clear on the GitHub that the Node-RED comments need to be used (to paste over the default files).

Tangential question, do you know a resource or two that explains how to implement something like this GUI: https://codepen.io/ettrics/pen/QbPEeg

This is what I get if copy/pasting un-edited:

This is what they have:

I'm guessing I need to debug errors for the HTML, SCSS and JS...

Lots of ideas here: vuejs drag drop at DuckDuckGo

And some more - specific to bootstrap-vue: bootstrap-vue drag drop at DuckDuckGo

So lots of choices.

The layout is relatively easy when using bootstrap-vue and its grid layout.

I keep meaning to do something like this but too many other priorities keep getting in the way!

1 Like

Do you have an idea why the HTML, SCSS and JS do not work from the dragula example? For instance, do the code from the example use a different compiler or something?

Did you check the settings for the Pen? There are a lot of things that get loaded. Most specifically, you cannot just load an scss file because your browser doesn't know what to do with it.

Hi there, I have just tried this so if you are just trying this for fun there are a few bits that you may have missed, (otherwise as TotallyInformation has said there are better ways of achieving drag & drop.

  1. The compiled SCSS has to be converted to CSS (did it using on-line conversion site). Check for errors after though.
  2. You have to add <script src="https://s3-us-west-.amazonaws.com/s.cdpn.io/45226/dragula.min.js"></script>
    into the index.html file (click on the wheel in each section of the codepen page to see additional info)
  3. I haven't worked out how to get the js stuff working but the JQuery example in the uibuilder wiki is a probably a good start.

I have the page showing on screen as in the example but as I said so far no luck on the code

1 Like

Ugh, there are some nasty hidden errors in that code as well.

The link for Dragula is:

<script src='https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.3/dragula.min.js'></script>

index.html

<html><head>
    <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="./index.css" media="all">

</head><body>

.... ALL THE STUFF FROM THE EXAMPLE GOES HERE ...

<script src="./uibuilderfe.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.3/dragula.min.js'></script>

</body></html>

index.css

* {
  box-sizing: border-box;
}
body {
  background: #33363D;
  color: white;
  font-family: 'Lato';
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
}
.drag-container {
  max-width: 1000px;
  margin: 20px auto;
}
.drag-list {
  display: flex;
  align-items: flex-start;
}
@media (max-width: 690px) {
  .drag-list {
    display: block;
  }
}
.drag-column {
  flex: 1;
  margin: 0 10px;
  position: relative;
  background: rgba(0, 0, 0, 0.2);
  overflow: hidden;
}
@media (max-width: 690px) {
  .drag-column {
    margin-bottom: 30px;
  }
}
.drag-column h2 {
  font-size: 0.8rem;
  margin: 0;
  text-transform: uppercase;
  font-weight: 600;
}
.drag-column-on-hold .drag-column-header, .drag-column-on-hold .is-moved, .drag-column-on-hold .drag-options {
  background: #FB7D44;
}
.drag-column-in-progress .drag-column-header, .drag-column-in-progress .is-moved, .drag-column-in-progress .drag-options {
  background: #2A92BF;
}
.drag-column-needs-review .drag-column-header, .drag-column-needs-review .is-moved, .drag-column-needs-review .drag-options {
  background: #F4CE46;
}
.drag-column-approved .drag-column-header, .drag-column-approved .is-moved, .drag-column-approved .drag-options {
  background: #00B961;
}
.drag-column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
}
.drag-inner-list {
  min-height: 50px;
}
.drag-item {
  margin: 10px;
  height: 100px;
  background: rgba(0, 0, 0, 0.4);
  transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
.drag-item.is-moving {
  transform: scale(1.5);
  background: rgba(0, 0, 0, 0.8);
}
.drag-header-more {
  cursor: pointer;
}
.drag-options {
  position: absolute;
  top: 44px;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 10px;
  transform: translateX(100%);
  opacity: 0;
  transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
.drag-options.active {
  transform: translateX(0);
  opacity: 1;
}
.drag-options-label {
  display: block;
  margin: 0 0 5px 0;
}
.drag-options-label input {
  opacity: 0.6;
}
.drag-options-label span {
  display: inline-block;
  font-size: 0.9rem;
  font-weight: 400;
  margin-left: 5px;
}
/* Dragula CSS  */
.gu-mirror {
  position: fixed !important;
  margin: 0 !important;
  z-index: 9999 !important;
  opacity: 0.8;
  list-style-type: none;
}
.gu-hide {
  display: none !important;
}
.gu-unselectable {
  -webkit-user-select: none !important;
  -moz-user-select: none !important;
  -ms-user-select: none !important;
  user-select: none !important;
}
.gu-transit {
  opacity: 0.2;
}
/* Demo info */
.section {
  padding: 20px;
  text-align: center;
}
.section a {
  color: white;
  text-decoration: none;
  font-weight: 300;
}
.section h4 {
  font-weight: 400;
}
.section h4 a {
  font-weight: 600;
}

index.js

dragula([
	document.getElementById('1'),
	document.getElementById('2'),
	document.getElementById('3'),
	document.getElementById('4'),
	document.getElementById('5')
])

.on('drag', function(el) {
	
	// add 'is-moving' class to element being dragged
	el.classList.add('is-moving');
})
.on('dragend', function(el) {
	
	// remove 'is-moving' class from element after dragging has stopped
	el.classList.remove('is-moving');
	
	// add the 'is-moved' class for 600ms then remove it
	window.setTimeout(function() {
		el.classList.add('is-moved');
		window.setTimeout(function() {
			el.classList.remove('is-moved');
		}, 600);
	}, 100);
});


var createOptions = (function() {
	var dragOptions = document.querySelectorAll('.drag-options');
	
	// these strings are used for the checkbox labels
	var options = ['Research', 'Strategy', 'Inspiration', 'Execution'];
	
	// create the checkbox and labels here, just to keep the html clean. append the <label> to '.drag-options'
	function create() {
		for (var i = 0; i < dragOptions.length; i++) {

			options.forEach(function(item) {
				var checkbox = document.createElement('input');
				var label = document.createElement('label');
				var span = document.createElement('span');
				checkbox.setAttribute('type', 'checkbox');
				span.innerHTML = item;
				label.appendChild(span);
				label.insertBefore(checkbox, label.firstChild);
				label.classList.add('drag-options-label');
				dragOptions[i].appendChild(label);
			});

		}
	}
	
	return {
		create: create
	}
	
	
}());

var showOptions = (function () {
	
	// the 3 dot icon
	var more = document.querySelectorAll('.drag-header-more');
	
	function show() {
		// show 'drag-options' div when the more icon is clicked
		var target = this.getAttribute('data-target');
		var options = document.getElementById(target);
		options.classList.toggle('active');
	}
	
	
	function init() {
		for (i = 0; i < more.length; i++) {
			more[i].addEventListener('click', show, false);
		}
	}
	
	return {
		init: init
	}
}());

createOptions.create();
showOptions.init();

That has the right layout but the drag/drop isn't working for some reason. Possibly I used a version of Dragula later than the one in the example.

1 Like

I used the one shown on the codepin page and the drag and drop works (try dragging a cell from one column to another - they change colour before going back to black), just that the cells are empty as clicking on the vertical ellipses does not pop out a selection box - not that this seems to do anything in the example.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.