Dashboard 2 ui-template how to access msg.payload

I have been banging my head against the wall trying to find a way to get access to msg.payload that is being passed into ui-template in a way that I can use it. The concept is to replace yyyymmdd in the src string for the iframe in this code with msg.payload.


```<template>
  <v-row>
    <v-col cols="3" v-for="stock in ['SPY', 'AAPL', 'QQQ', 'TQQQ']" :key="stock">
      <v-card>
        <v-card-text>
          <iframe :src=`/20240614_${stock}_output.html` width="800px" height="500px"></iframe>
        </v-card-text>
      </v-card>
    </v-col>
  </v-row>
</template>```

I have tried many variations with on mount, using context variables,this.$store.state.input.payload {payload}, {{payload}} and many others and cannot find a way to do this. it seems like it should be simple. I am sure the msg.payload is coming in as I can pass it through to a debug on the output...

In DB 1 I use the scope. Isn't that available in DB 2? Like this example from one of my DB 1 setups:

// Watch the payload and update corresponding picture
(function(scope) {
    scope.$watch('msg', function(msg) {
        //alert(msg);
        inMessage(msg.payload);
    });
})(scope);

EDIT: From the help text of DB 2 ui_template node I read the following:

Reading Node Input
Whenever a ui-template receives a msg in Node-RED, that is automatically assigned to the msg variable in the template.

Additionally, we also bind a computed variable value to msg.payload. This means that you can access the msg object in your template, as well as binding the value variable to any widgets you use.

Such an example would be:

<div>
    <h2>Latest <code>msg</code> received:</h2>
    <pre>{{ msg }}</pre>
</div>

When binding to an attribute, everything inside the attribute value is effectively JavaScript & all attribute values must be quoted, therefore:

<iframe :src="`/${msg.payload}_${stock}_output.html`" width="800px" height="500px"></iframe>

Working with Variables

Some useful docs to help you get started are detailed here: Template ui-template | Node-RED Dashboard 2.0

Your Example

In your specific case here, the:

<iframe :src=`/20240614_${stock}_output.html` width="800px" height="500px"></iframe>

is very close, you need to wrap the :src value in double quotation marks:

<iframe :src="`/20240614_${stock}_output.html`" width="800px" height="500px"></iframe>

anything inside those quotation marks, when the attribute is marked as dynamic with (:) is just treated as Javascript.

This would then look on the widget for a variable named stock.

To replace the date, using the built-in msg variable, you could then write:

<iframe :src="`/${msg.payload}_${stock}_output.html`" width="800px" height="500px"></iframe>

Prompted by this, I've also just opened a Pull Request to improve our documentation in this space: Docs: Clearer documentation on using variables inside a ui-template by joepavitt · Pull Request #982 · FlowFuse/node-red-dashboard · GitHub

2 Likes

Thanks for the input very helpful! The previous post is a solution that works. Not sure if accepted behaviour allows for an expansion in scope on the question, if so my apologies, I won't do it again.

Taking into account that the code inside the " " is considered to be javascript I have been trying to handle the situation where on startup the msg.payload is "undefined" until the user selects a data in the date select widget. The code below includes the working code and several version of attempts to utilize ternary operator, none of which work and are commented out in the snippet.

<template>
  <v-row>
    <v-col cols="3" v-for="stock in ['SPY', 'AAPL', 'QQQ', 'TQQQ']" :key="stock">
      <v-card>
        <v-card-text>
          <iframe :src="`/${msg.payload}_${stock}_output.html`" width="800px" height="500px"></iframe>
          <!-- <iframe :src="`/${msg && msg.payload ? msg.payload : getTodayDate()}_${stock}_output.html`" width="800px" height="500px"></iframe> -->
          <!-- <iframe :src="`/${msg.payload ? msg.payload : getTodayDate()}_${stock}_output.html`" width="800px" height="500px"></iframe> -->
          <!-- <iframe :src="`/{${msg.payload==='undefined'? getTodayDate():msg.payload}_${stock}_output.html`" width="800px" height="500px"></iframe> -->
        </v-card-text>
      </v-card>
    </v-col>
  </v-row>
</template>

<script>
function getTodayDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // Months are 0-based
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}${month}${day}`;
}
</script>

The items with the ternary operator each do not render anything other that a single bar. Typically when the undefined occurs the card displays with a message that Get can't find the file.

Thanks in advance for any information.

PS: Thinking this error on console may have something to do with it

hook-exec.js:1 Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-gPjlli1HEdLlR0AZTY971/wQVOdSkl9mEinLnxrPpJw='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

Try this. I have had to make some additions because I do not have your data. This shows the filename as text but you will get the idea. You should also probably put in some error checking for msg.payload

<template>


      <v-card>
        <v-card-text>
          <!-- <iframe :src="fileName" width="800px" height="500px"></iframe> -->
          <p> {{fileName}}</p>
          <!-- <iframe :src="`/${msg.payload ? msg.payload : getTodayDate()}_${stock}_output.html`" width="800px" height="500px"></iframe> -->
          <!-- <iframe :src="`/{${msg.payload==='undefined'? getTodayDate():msg.payload}_${stock}_output.html`" width="800px" height="500px"></iframe> -->
        </v-card-text>
      </v-card>
 
  
</template>

<script>
    export default {
        data() {
            // define variables available component-wide
            // (in <template> and component functions)
            return {
              stock: 'New Stuff',  //Note you do not need this.
              fileDate: ""

            }
        },

        watch: {
            // watch for message
            msg: function () {
                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)
                if (this.msg.payload != undefined) {
                    this.fileDate = this.msg.payload
                }
            },

        },

        computed: {
            // automatically compute this variable
            // whenever VueJS deems appropriate
           fileName: function() {
               return `/${this.fileDate}_${this.stock}_output.html`
           },

        },

        methods: {
             // expose a method to our <template> and Vue Application
           getTodayDate: function() {
                const today = new Date()
                const year = today.getFullYear()
                const month = String(today.getMonth() + 1).padStart(2, '0') // Months are 0-based
                const day = String(today.getDate()).padStart(2, '0')
  
                this.fileDate = `${year}${month}${day}`
           },

        },
    
        mounted() {
            // listen for incoming msg's from Node-RED
            // note our topic is "msg-input" + the node's unique ID
            this.getTodayDate()

        },

        unmounted() {
            // code here when the component is removed from the Dashboard
            // i.e. when the user navigates away from the page
        }
    }
</script>

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