Dialog not appering in DB2, only the page is faded

I am trying to implement a v-dialog in DB2. I took one of the example and added to a template node:

<template>
        <v-btn color="primary" @click="dialog = true">
            Open Dialog
        </v-btn>


            <v-dialog width="auto" v-model="dialog">
                <template>
                    <v-card color="white">
                        <v-toolbar color="primary" title="Opening from the bottom"></v-toolbar>
                        <v-card-text>
                            <div class="text-h2 pa-12">Hello world!</div>
                        </v-card-text>
                        <v-card-actions class="justify-end">
                            <v-btn variant="text" @click="dialog = false">Close</v-btn>
                        </v-card-actions>
                    </v-card>
                </template>
            </v-dialog>


</template>

<script>
    export default {
        data() {
            // define variables available component-wide
            // (in <template> and component functions)
            return {
                dialog: false,
            }
        },
        watch: {
            msg: function(){
                if(this.msg.payload != undefined){
                    this.dialog = true;
                }
            }
        }
    }
</script>

I set up this template as "Widget (Group-Scoped)". The "Open Dialog" button appears as expected, but when the page load the colors are faded as if the dialog is being displayed. When I click the fade is removed, hence I suspect the dialog was on. Although in the data section dialog: false, is set as default.
Also when I press on the "Open Dialog" button, the dialog itself is not shown, just the page gets faded.

I changed the template to be Page scoped, removed the v-btn from the code but the behaviour is the same. I can correctly trigger dialog by sending a msg.payload = true, but again, colors are faded but the dialog is not appearing on the screen.

Any ideas? Is this a style issue?

Remove this template tag.

Thanks, I am not sure why there was a second template node in the v-dialog. Probably my copy and paste mistake.
But the dialog still opens when I open the page the group is on. I also added a mounted method, I though that one was missing but still did not help:

        mounted() {
        // code here when the component is first loaded
            this.dialog = false
        },

Triggering from an external message works just fine.

Probably because of last msg replayed to the node and that triggers the dialog.

There is of course many ways how and why to show modal dialog. As the content can be surely almost everything it can go crazy ...

But for common and simple usage something like this should fit into DB2 world. It has no button involved for opening. It does take title and content from incoming msg.payload and then immediately sends out msg with payload = undefined to clear replay message. The data cannot be cleared during closing because of little animations on closing, they will jitter otherwise.

It is reasonable to have it ui scoped so no matter the page it can be used.
image

<template>
    <v-dialog width="auto" v-model="dialog">    
      <v-card color="white" v-click-outside="{handler: onClickOutside}">
        <v-toolbar color="primary">
          <v-card-title>
            <span>{{dialogData.title}}</span>
          </v-card-title>
        </v-toolbar>        
        <v-card-text>
          <div class="text-h2 pa-12">{{dialogData.content}}</div>
        </v-card-text>
        <v-card-actions class="justify-end">
          <v-btn variant="text" @click="closeDialog">Close</v-btn>
        </v-card-actions>
      </v-card>    
  </v-dialog>
</template>

<script>
export default {
  data() {
    return {
        dialogData:null
    }
  },
  watch: {
      msg: function(){
          if(this.msg.payload != undefined){                  
              this.dialogData = this.msg.payload;
              this.dialogData.shown = false
              this.send({payload:undefined})
          }
      }
  },
  methods:{
    closeDialog:function(){
      this.dialogData.shown = true;
    },
    onClickOutside () {
      this.dialogData.shown = true;
    },
  },
  computed : {
    title: function () {
      return this.dialogData?.title ?? ""
    },
    content:function(){
      return this.dialogData?.content ?? ""
    },
    dialog: function (){
      return this.dialogData?.shown == false
    }        
  },
  unmounted () {
    this.dialogData = null
  }
}
</script>
2 Likes

@hotNipi sorry to ask, but did you try this? It is not working for me.

Not working doesn't ring any bell. Can you be more specific

1 Like

You have to give it a correctly structured payload, which you can determine from the code.

1 Like

Sorry, I did not read the instruction properly. It works now.

Can I ask some questions about this? Just to make sure I understand this correctly?

  1. The v-dialog has v-model="dialog" and here the dialog is the dialog function in the computed section? It it is a variable called dialog and when Vue is trying to use this value the corresponding computed function is called to evaluate it's value?
  2. Why did you add the title and content functions? I understand that it sets the values to "", but these values would be set from msg.payload before the the dialog is shown, correct? Or is this just for good housekeeping? Or set the value in case the payload does not contain either the title or the content?
  3. When I was passing a simple string in the payload instead of the object the dialog did not appear. Was this because JS code in the watch function failed at line this.dialogData.shown = false; when trying to set an attribute to a String?
  4. What was the reason for defining this.dialogData.shown = false, instead of this.dialogData.show = true or this.dialogData.visible = true. Is this a convension I am missing here?
  5. To be honest I still don't understand why this.send({payload:undefined}); was necessary? If I set the template node not to pass through the input message, it does not appear on the output anyway, right?

Computed is so to say "clever property". For this case they are simple but as for base of creating more complex thing it can be much more complicated - but still it returns a boolean or whatever it needs to be. You just can't fit really complex porterites into the template - that's why computed exist. For property dialog it is still same - "show or to not show".

As I stated - the dialog can be absolutely crazy thing. For title and content it is overkill maybe but if you create a form where input via msg provides only indication how to build that form, you'll need to manage that info to the real properties. So just follow the strategy to not fall into troubles when it grows.

That falls into category "misconfiguration" cos for this solution the payload must be structured properly. Validation over the incoming msg is always key thing to create good product.

It is just my choice of logic. But overall this solution needs a boolean which is in one state before showing and turns over when dialog forced to close (no mater the closing trigger)

As for DB1 there was replayMessage - it is same or similar for DB2. So if there is connection change - the server side does reply with last known msg. So if you opened dialog at some point of time - the last msg in reply conditions opens the dialog. That is unwanted, but unavoidable by any tools. The trick is to ruin that last msg to have no payload. And then if it comes - it is filtered out by if(this.msg.payload != undefined){ So no more unwanted messages to trigger the dialog. This trick can be used anywhere if you want to avoid replayMessages.

1 Like

Why does the dialog not appear if this.send({payload:undefined}); is removed?
I am massaging the template to make an OK/Cancel dialog, so I have to send the appropriate message when one of the buttons is clicked. That works, but if I remove the send in the watch block then the dialog does not appear at all.

The replay is not and should not connected to the outcome of the user choice. Avoiding the replay is just for to not get same message popped up when it is not intentional.

I have just realised that I messed up removing the send line. Getting rid of that does not stop the dialog appearing.

Do you mean I should not use send() to send a message out of the template? If not then how should I do it? It does appear to work as I have written it.

Msg in and msg out should not use same msg object to not confuse the logic.

I have no choice to test it now but I think that after any operation with such modal the dialog data should be disconnected from msg so whatever next msg will bring the dialog it is pure.

The property this.msg is bound to the widget. It can be ruined or overwritten so think both ways and you figure that out.

I'm still sitting with my dog. No big screens and node red physically.

Oh, I think I see now what you mean. I can send a message but I should use a different object. Unfortunately one of the requirements of an ok/cancel dialog is that it does not mess with other properties of the message so that the details of whatever it is that is being ok/cancelled is passed on with the message. I am not sure how to deal with that. This is what I have so far and it appears to work correctly, though I need to refine some aspects of it.

<template>
    <v-dialog width="auto" v-model="dialog">
        <v-card color="white" v-click-outside="{handler: onClickOutside}">
            <v-toolbar color="primary">
                <v-card-title>
                    <span>{{dialogData.title}}</span>
                </v-card-title>
            </v-toolbar>
            <v-card-text>
                <div class="text-h2 pa-12">{{dialogData.content}}</div>
            </v-card-text>
            <v-card-actions class="justify-end">
                <v-btn variant="text" @click="cancelDialog">Cancel</v-btn>
                <v-btn  @click="okDialog">OK</v-btn>
            </v-card-actions>
        </v-card>
    </v-dialog>
</template>

<script>
    export default {
  data() {
    return {
        dialogData:null
    }
  },
  watch: {
      msg: function(){
          if(this.msg.payload != undefined){                  
              this.dialogData = this.msg.payload;
              this.dialogData.shown = false
              this.msg.payload = undefined
              this.msg = msg
              /*this.send({payload:undefined})*/
          }
      }
  },
  methods:{
    okDialog:function(){
      this.dialogData.shown = true;
      this.msg.action = "ok"
      this.send(this.msg);
    },
    cancelDialog:function(){
      this.dialogData.shown = true;
      this.msg.action = "cancel"
      this.send(this.msg);
    },
    onClickOutside () {
      this.dialogData.shown = true;
      this.msg.action = "cancel"
      this.send(this.msg);
    },
  },
  computed : {
    title: function () {
      return this.dialogData?.title ?? ""
    },
    content:function(){
      return this.dialogData?.content ?? ""
    },
    dialog: function (){
      return this.dialogData?.shown == false
    }        
  },
  unmounted () {
    this.dialogData = null
  }
}
</script>

I am still ensuring that payload is undefined in order to stop it showing the dialog on refresh.

Clearing the payload after each send maybe another choice...

I do remember slight that there was discussion about the discarding replay message node or widget basis with DB1. Don't remember where it ended but should be possible cos it's just a coding. If such checkbox for widget would exist, there's no such fighting. @joepavitt to think about...

I've never liked message reply. Was kinda sad we ended up with this again in db2 (tho I don't know what the alternative is)

However, at minimum, the replay messages should identify this (perhaps a special ._msgReplay flag) something we can use to discard (or keep) the data. Do we have this in db2?

Obviously a checkbox would be superior.

I am popping up my dialog when the user clicks a button. How can I make it so that the dialog only appears on the session where the button was clicked? As it is at the moment it appears on all connected sessions and must be cleared separately on each. I am already making sure that msg._client is present in the msg sent to the template.

I don't know if this is a behaviour carried over from DB1 Colin. Will have to ask @joepavitt

Is there anything in the msg that looks to identify the session where you can compare it to a local variable?