Inject Chart.js array data into UI Builder

Hello,

I am trying to learn how to use UI Builder and I have successfully installed v6. I have set up a test flow where I am using the Vue and Bootstrap template. At this point I am able to create a a simple web page. I had developed a Chart.js object and I can render the data if I manually add the array data to the html page.

However if I send a payload over with the data, the page does not render. I was trying to use the examples Julian provided to modify in order render the Chart.js data.

I am hoping some one can advise how to inject the Chart.js data into UI Builder. Here is the test flow:

[{"id":"6aa52a794a7301b9","type":"inject","z":"05cf8d9f6c950756","name":"","props":[{"p":"payload"},{"p":"legend1","v":" # of Votes","vt":"str"},{"p":"title","v":"Lightning Strikes","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[12,10,3,5,2,3]","payloadType":"json","x":130,"y":260,"wires":[["8da5f3d1ce371396","0f2f6f13b4093138","29fe17e949535a59"]]},{"id":"631723fcdfa58ab9","type":"inject","z":"05cf8d9f6c950756","name":"","props":[{"p":"payload"},{"p":"legend1","v":" # of Votes","vt":"str"},{"p":"title","v":"Lightning Strikes","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[12,19,3,5,2,3]","payloadType":"json","x":130,"y":300,"wires":[["8da5f3d1ce371396","0f2f6f13b4093138"]]},{"id":"0f2f6f13b4093138","type":"uibuilder","z":"05cf8d9f6c950756","name":"","topic":"","url":"uitest","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"vue","extTemplate":"totallyinformation/uib-test/","showfolder":false,"reload":true,"sourceFolder":"src","deployedVersion":"6.0.0","showMsgUib":false,"credentials":{},"x":420,"y":260,"wires":[["29fe17e949535a59"],["29fe17e949535a59"]]}]

Currently I have the javascript element for the Charjs object in the index.html file. I am not sure if that is the best practice. Also what is the best practice for displaying multiple elements from the flow on the UI

Hello Mick,

you have to also share your html and javascript files otherwise its impossible to know why the chart is not rendering. (The flow alone doesnt include this code)

Installed packages

index.html

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
</head>
<nav class="navbar navbar-dark bg-dark navbar-expand-lg">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">W8BE</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="#">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Link</a>
                </li>
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
                        aria-expanded="false">
                        Dropdown
                    </a>
                    <ul class="dropdown-menu">
                        <li><a class="dropdown-item" href="#">Action</a></li>
                        <li><a class="dropdown-item" href="#">Another action</a></li>
                        <li>
                            <hr class="dropdown-divider">
                        </li>
                        <li><a class="dropdown-item" href="#">Something else here</a></li>
                    </ul>
                </li>
                <li class="nav-item">
                    <a class="nav-link disabled">Disabled</a>
                </li>
            </ul>
            <form class="d-flex" role="search">
                <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-success" type="submit">Search</button>
            </form>
        </div>
    </div>
</nav>
<body>
    <h1>Hello, world!</h1>
    <pre id="msg" v-html="formatLastRcvd" class="syntax-highlight">Waiting for a message from Node-RED</pre>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous">
    </script>
<div class="chart-container" style="position: relative; height:10vh; width:10vw,maintainAspectRatio:true">
    <canvas id="myChart1"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
    new Chart(document.getElementById("myChart1"), {
    type: 'bar',
    data: {
      labels: ['60', '30', '15', '10', '5', '1'],
      datasets: [
        {
          label: "Strikes/min",
          backgroundColor: "rgba(38, 91, 95, .5000)",
          borderColor: 'rgba(20, 20, 20, .5)',  
          borderRadius: 7.5,
          borderWidth: 2,
          data: [
          // {{msg}}
           12,10,3,5,2,3  // test data

          ]
        }      ]
    },
    options: {
       scales: {
        x:{
          grid:{
          drawOnChartArea:false
          },
          ticks:{
            font:{
              size:8
            }
          }
        },
        y:{
          grid:{
            drawOnChartArea:false
          },
          ticks:{
            font:{
            size:8
            }
          }
        }
      },
      plugins:  {
       legend: {
        display: false,
       labels: {
        fontColor: 'rgb(255, 99, 132)'
       }
      },  //end of legend
      title: {
       display: false,
        text: '{{{payload.title}}}'
      }, // end of title
      },
  }
});

</script>

</body>

</html>

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2021-2022 Julian Knight (Totally Information)

  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.
*/
'use strict'

/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    data() { return {

        startMsg    : 'Vue has started, waiting for messages',
        feVersion   : '',
        counterBtn  : 0,
        inputText   : null,
        inputChkBox : false,
        socketConnectedState : false,
        serverTimeOffset     : '[unknown]',
        imgProps             : { width: 75, height: 75 },

        msgRecvd    : '[Nothing]',
        msgsReceived: 0,
        msgCtrl     : '[Nothing]',
        msgsControl : 0,

        msgSent     : '[Nothing]',
        msgsSent    : 0,
        msgCtrlSent : '[Nothing]',
        msgsCtrlSent: 0,

        isLoggedOn  : false,
        userId      : null,
        userPw      : null,
        inputId     : '',

    }}, // --- End of data --- //

    computed: {

        hLastRcvd: function() {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd !== 'string') return 'Last Message Received = ' + msgRecvd
            return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function() {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function() {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function() {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },

    }, // --- End of computed --- //

    methods: {

        // Called from the increment button - sends a msg to Node-RED
        increment: function(event) {
            console.log('Button Pressed. Event Data: ', event)

            // Increment the count by one
            this.counterBtn += 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'message': this.inputText,
                    'inputChkBox': this.inputChkBox
                }
            } )

        }, // --- End of increment --- //

        // REALLY Simple method to return DOM events back to Node-RED. See the 2nd b-button on the default html
        doEvent: uibuilder.eventSend,

        // return formatted HTML version of JSON object
        syntaxHighlight: function(json) {
            json = JSON.stringify(json, undefined, 4)
            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
                var cls = 'number'
                if ((/^"/).test(match)) {
                    if ((/:$/).test(match)) {
                        cls = 'key'
                    } else {
                        cls = 'string'
                    }
                } else if ((/true|false/).test(match)) {
                    cls = 'boolean'
                } else if ((/null/).test(match)) {
                    cls = 'null'
                }
                return '<span class="' + cls + '">' + match + '</span>'
            })
            return json
        }, // --- End of syntaxHighlight --- //

    }, // --- End of methods --- //

    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        // Example of retrieving data from uibuilder
        this.feVersion = uibuilder.get('version')

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names
         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.
         * @param {string=} ioPath Optional. changes self.ioPath from the default
         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.
         */
        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

        //console.log(this)

    }, // --- End of created hook --- //

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){

        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        var app = this  // Reference to `this` in case we need it for more complex functions

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        uibuilder.onChange('msg', function(msg){
            console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            app.msgRecvd = msg
            app.msgsReceived = uibuilder.get('msgsReceived')
        })

        //#region ---- Debug info, can be removed for live use ---- //

        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)
            app.msgCtrl = msg
            app.msgsControl = uibuilder.get('msgsCtrl')
        })

        /** You probably only need these to help you understand the order of processing
         * If a message is sent back to Node-RED, we can grab a copy here if we want to
         */
        uibuilder.onChange('sentMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)
            app.msgSent = msg
            app.msgsSent = uibuilder.get('msgsSent')
        })

        /** If we send a control message to Node-RED, we can get a copy of it here */
        uibuilder.onChange('sentCtrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)
            app.msgCtrlSent = msg
            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')
        })

        /** If Socket.IO connects/disconnects, we get true/false here */
        uibuilder.onChange('ioConnected', function(connected){
            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)
            app.socketConnectedState = connected
        })
        /** If Server Time Offset changes */
        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){
            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)
            app.serverTimeOffset = serverTimeOffset
        })

        /** If user is logged on/off */
        uibuilder.onChange('isAuthorised', function(isAuthorised){
            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)
            //console.log('authData: ', uibuilder.get('authData'))
            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))
            app.isLoggedOn = isAuthorised
        })

        //#endregion ---- Debug info, can be removed for live use ---- //

    }, // --- End of mounted hook --- //

}) // --- End of app1 --- //

// EOF

index.css

/* Load the defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/src/uib-styles.css` */
@import url("./uib-styles.css");

/* Cloak elements on initial load to hide the possible display of {{ ... }} 
 * Add to the app tag or to specific tags
 * To display "loading...", change to the following:
 *    [v-cloak] > * { display:none }
 *    [v-cloak]::before { content: "loading…" }
 */
[v-cloak] { display: none; }

It seems that you are mixing in your code, Vue front end language with plain vanilla javascript.
if you are not familiar with Vue then its better not to use it at all.

another thing i noticed is that in uibuilder.onChange, which is where the msg is received when you send it to the uibuilder node .. you dont seem to be doing anything to update the chart ?

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Bootstrap demo</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
      crossorigin="anonymous"
    ></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script defer src="./uibuilder.iife.min.js"></script>
    <script defer src="./index.js"></script>
  </head>

  <body>
    <nav class="navbar navbar-dark bg-dark navbar-expand-lg">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">W8BE</a>
        <button
          class="navbar-toggler"
          type="button"
          data-bs-toggle="collapse"
          data-bs-target="#navbarSupportedContent"
          aria-controls="navbarSupportedContent"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Dropdown </a>
              <ul class="dropdown-menu">
                <li><a class="dropdown-item" href="#">Action</a></li>
                <li><a class="dropdown-item" href="#">Another action</a></li>
                <li>
                  <hr class="dropdown-divider" />
                </li>
                <li><a class="dropdown-item" href="#">Something else here</a></li>
              </ul>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled">Disabled</a>
            </li>
          </ul>
          <form class="d-flex" role="search">
            <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" />
            <button class="btn btn-outline-success" type="submit">Search</button>
          </form>
        </div>
      </div>
    </nav>

    <h1>Hello, world!</h1>
    <div class="chart-container" style="position: relative; height:90vh; width:90vw;">
      <canvas id="myChart1" width="400" height="100"></canvas>
    </div>

  </body>
</html>

index.js

/* eslint-disable strict */
/* jshint browser: true, esversion: 6, asi: true */
/* globals uibuilder */
// @ts-nocheck

/** Minimalist code for uibuilder and Node-RED */
"use strict";

// run this function when the document is loaded
window.onload = function () {

// create chart  
  let barChart = new Chart(document.getElementById("myChart1"), {
    type: "bar",
    data: {
      labels: ["60", "30", "15", "10", "5", "1"],
      datasets: [
        {
          label: "Strikes/min",
          backgroundColor: "rgba(38, 91, 95, .5000)",
          borderColor: "rgba(20, 20, 20, .5)",
          borderRadius: 7.5,
          borderWidth: 2,
          data: [
            12,
            10,
            3,
            5,
            2,
            3, // test data
          ],
        },
      ],
    },
    options: {
      scales: {
        x: {
          grid: {
            drawOnChartArea: false,
          },
          ticks: {
            font: {
              size: 8,
            },
          },
        },
        y: {
          grid: {
            drawOnChartArea: false,
          },
          ticks: {
            font: {
              size: 8,
            },
          },
        },
      },
      plugins: {
        legend: {
          display: false,
          labels: {
            fontColor: "rgb(255, 99, 132)",
          },
        }, //end of legend
        title: {
          display: true,
          text: "myChart",
        }, // end of title
      },
    },
  });

  // Listen for incoming messages from Node-RED
  uibuilder.onChange("msg", function (msg) {
    console.info("[indexjs:uibuilder.onChange] msg received from Node-RED server:", msg);

if (msg && msg.topic === "updateChart") {
    barChart.data.labels = msg.labels;
    barChart.data.datasets[0].data = msg.payload;
    barChart.options.plugins.title.text = msg.title;
    barChart.update(); // re-render chart
}

  });
};

Flow with some changes in the msg structure :

[{"id":"6aa52a794a7301b9","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"labels","v":"[\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]","vt":"json"},{"p":"legend1","v":" # of Votes","vt":"str"},{"p":"title","v":"Lightning Strikes","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"updateChart","payload":"[12,10,3,5,2,3]","payloadType":"json","x":460,"y":2520,"wires":[["0f2f6f13b4093138"]]},{"id":"0f2f6f13b4093138","type":"uibuilder","z":"54efb553244c241f","name":"","topic":"","url":"uitest","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"vue","extTemplate":"totallyinformation/uib-test/","showfolder":false,"reload":true,"sourceFolder":"src","deployedVersion":"6.0.0","showMsgUib":false,"x":710,"y":2560,"wires":[[],[]]},{"id":"615c9b559e4f9d68","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"labels","v":"[\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]","vt":"json"},{"p":"legend1","v":" # of Votes","vt":"str"},{"p":"title","v":"Lightning Strikes","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"updateChart","payload":"[120,190,30,50,20,30]","payloadType":"json","x":440,"y":2600,"wires":[["0f2f6f13b4093138"]]}]
1 Like

Thank you. I am going to deep dive your code. One thing I noticed in your code is that you used a hard coded default ( I think)…

 data: [
                        12,
                        10,
                        3,
                        5,
                        2,
                        3, // test data
                    ],

If true then this code over writes the default data?

barChart.data.datasets[0].data = msg.payload;

Regards,
Mick

yes .. in the code you shared there were some initial values .. i left them there
if you dont need them just initialize data as empty array [].
data: [] and also i believe the labels: []

Thanks... so only one more issue to work out for this code is why the page does not render the correct color or size as the one where I had the data in the index.html. I appreciate your help.

Regards

Not read through all your code but I don't think you are loading everything you should. Here is an example head section that loads everything in the right order:

<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>VueJS + bootstrap-vue simple template - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - VueJS + bootstrap-vue simple template">

    <link rel="icon" href="./images/node-blue.ico">

    <!-- #region Supporting CSS. -->
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="./index.css" media="all"><!-- Your own CSS -->
    <!-- #endregion -->

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed -->
    <script defer src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script>
    <script defer src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script>
    <script defer src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
    <!-- #endregion -->
    
</head><body>

Honestly, you will find it easier to develop if you keep your code looking neat and give yourself a bit of breathing room around different parts. That way it is much easier to spot mistakes.

Also, you use the defer option on scripts when you put them in the head, not when you put them in the body. Using defer is the same as putting them at the end of your body.

Thank you all. As you can see I am drinking through a fire hose. I figured out the size issue on the div so I am on a roll now. I appreciate all the work you have put in to UI Builder.

Regards,

Mick

as Julian showed .. i missed loading in index.html your index.css styles .. and if you dont load a css file uibuilder loads a uib-brand.css style for you and that may clash with the bootstrap styles.

so add to the head section of index.html

<link type="text/css" rel="stylesheet" href="./index.css" media="all">

and also remove the @import url("./uib-styles.css"); from your index.css

1 Like

Fixed in the next release :grin:

As you already know I am in over my head but you have start somewhere. Thanks

1 Like

It's great that you are having a go. We all started from nothing originally. :grin:

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