# Ui-template copy to clip - using ‘watch’ to retrieve other node data as well

**URL:** <https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092>\
**Category:** Dashboard\
**Tags:** dashboard-2\
**Created:** [5 January 2026 23:28 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092 "2026-01-05T23:28:50Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [5 January 2026 23:28 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/1 "2026-01-05T23:28:50Z")

</div>

How can a ui-template node reliably copy a simple text string to the clipboard?

The following JS code in the ui-template (Dashboard2) works with a button within the ui-template.

However, it is necessary for the ui-template node to receive the text string via a `msg` also. In the JS code, a `msg` is received via `watch`. This is confirmed by `console.log`.  
But the text string is not copied to the clipboard. The alert function says: “`undefined`”.

The console outputs:  
_“document.execCommand('cut'/'copy') wurde abgelehnt, weil es nicht von innerhalb einer kurz dauernden benutzergenerierten Ereignisbehandlung aufgerufen wurde.”._  
deepl says:  
_“document.execCommand(‘cut’/'copy') was rejected because it was not called from within a short-lived user-generated event handler.”_

```auto
[{"id":"71258c04f9bf025c","type":"inject","z":"b25c3ffa5d02edb9","name":"copyThis2Clip","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"copyThis2Clip","payload":"","payloadType":"date","x":230,"y":1180,"wires":[["2377519e41010f8f"]]},{"id":"2377519e41010f8f","type":"ui-template","z":"b25c3ffa5d02edb9","group":"cf2110ce4840bb3c","page":"","ui":"","name":"copy2clip_test","order":1,"width":0,"height":0,"head":"","format":"<template>\n \n <span> copy2Clip_2026-01-05</span>\n <v-btn color=\"primary\" @click=\"this.cClip('hello ')\">\n hello cClip\n </v-btn>\n</template>\n\n<script>\n export default {\n data() {\n return {\n ctext: \"testpattern\"\n };\n },\n\n watch: {\n msg(newMsg) {\n if (!newMsg) return;\n\n if (newMsg.topic === 'copyThis2Clip') {\n const test = (`watch:: ${new Date(newMsg.payload)}`)\n this.ctext = test;\n\n console.log(test);\n // this.cClip(test);\n\n setTimeout(() => {\n this.cClip(this.ctest);\n }, 100);\n newMsg = null;\n }\n }\n },\n\n methods: {\n\n cClip(test) {\n //copy_text(input) {\n // Get the text field\n // copyText = document.getElementById(input).innerText;\n var dummy = document.createElement(\"textarea\");\n document.body.appendChild(dummy);\n dummy.value = test;\n dummy.select();\n document.execCommand(\"copy\");\n document.body.removeChild(dummy);\n alert(\"Copied Data to Clipboard\\n\\n\" + test)\n },\n }\n }\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":440,"y":1180,"wires":[[]]},{"id":"cf2110ce4840bb3c","type":"ui-group","name":"Gruppe 1","page":"eb8696622e5576eb","width":"6","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"eb8696622e5576eb","type":"ui-page","name":"xPlorer_1","ui":"2f9f3adcdf082281","path":"/page6","icon":"home","layout":"grid","theme":"10d4154b82c88eef","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":4,"className":"","visible":true,"disabled":false},{"id":"2f9f3adcdf082281","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":"5","showDisconnectNotification":true,"allowInstall":true},{"id":"10d4154b82c88eef","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

```

Any advice?

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [6 January 2026 11:08 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/2 "2026-01-06T11:08:03Z")

</div>

The previous description does not show the complete situation.  
In fact, “`copy2clip`” works when it is triggered from the `ui-template` page, e.g., by a button.

However, the use case is that another, second modal page is called up from the **first** ui-template page. The data which needs to be copied is displayed on the **second** ui-template, the modal page. The user can select one of these data elements using click and thereby transfer it to the clipboard. _This does not work!_

Presumably, the access with `const dummy = document.createElement(“textarea”);` does not refer to the modal window, which is why `document.execCommand(‘copy’);` fails,  
but also does not throw a `console.error(“Copy failed !”, err);`

Is this a plausible explanation and how can it be done correctly?

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [6 January 2026 11:52 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/3 "2026-01-06T11:52:55Z")

</div>

The clipboard copy succeeds when the button is clicked, since the button click is a 'user gesture' (initiated by a user from the UI), which is allowed to do this.  
However, for security reasons, the browser blocks this operation if initiated by a non-UI event, such as a network event (the inject node sends the msg from the Node-red server to the dashboard input socket).

BTW, I would recommend using the native API instead of creating a temporary 'dummy' element. it will also give you an error message.

```auto
cClip(test) {
   navigator.clipboard.writeText(test)
   .then(() => {
      console.log(test+" copied to clipboard!");
   })
   .catch(err => {
      console.error("Failed to copy to clipboard", err);
   });
}

```

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [6 January 2026 11:57 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/4 "2026-01-06T11:57:43Z")

</div>

There is **no API, no trick, no hidden flag, no synthetic event, no focus hack** that can turn a programmatic action into a _trusted user gesture_.

This is a deliberate security boundary. If it were possible, any website could:

- copy spam into your clipboard
- overwrite passwords
- trigger downloads
- open popups
- interact with the OS

…without you clicking anything.

So browsers enforce a strict rule:

> **Only real, physical user actions (click, tap, keypress) count as trusted gestures.**

Everything else — timers, watchers, socket events, promises, async callbacks — is untrusted.

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [6 January 2026 12:05 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/5 "2026-01-06T12:05:19Z")

</div>

> [@gNeandr](#):
>
> However, the use case is that another, second modal page is called up from the **first** ui-template page. The data which needs to be copied is displayed on the **second** ui-template, the modal page. The user can select one of these data elements using click and thereby transfer it to the clipboard. _This does not work!_

AFAIK This should work. Let me check

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [6 January 2026 12:45 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/6 "2026-01-06T12:45:36Z")

</div>

The "modern" way fails:

`errorCaptured TypeError: can't access property "writeText", navigator.clipboard is undefined`

navigator has:  
userAgent: "Mozilla/5.0 (X11; Linux x86\_64; rv:144.0) Gecko/20100101 Firefox/144.0"

but no 'clipboard'

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [6 January 2026 13:03 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/7 "2026-01-06T13:03:02Z")

</div>

Interesting. So I guess you are using FireFox. It has stricter security, which seems to support `navigator.clipboard` only under HTTPS

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [6 January 2026 13:46 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/8 "2026-01-06T13:46:00Z")

</div>

Also with Brave, but that's also Firefox:  
`errorCaptured TypeError: Cannot read properties of undefined (reading 'writeText')`

`'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'`

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [6 January 2026 18:33 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/9 "2026-01-06T18:33:51Z")

</div>

The browser allows programmatic copy to the clipboard only when it is invoked from within a user gesture.  
As you mentioned, you can send an event from the inject node into the template, then open a modal window (`v-dialog`) from which you can then hit some button (I played with it and it works). Another way would be set visibility on/off to a text-area field & copy button on the main page itself.

In any case, it seems the issue you are facing is that unlike Edge & Chrome, programmatic copy to the clipboard in Firefox & Brave (in both `navigator.clipboard` and 'dummy' element) requires to be on HTTPS.

BTW, instead of using the `watch` mechanism (which has issues), I would just set a msg listener in the `mounted()` area and have full control of incoming messages.

```auto
mounted() {
    const vThis = this; // save current 'this', for use inside callbacks & function properties
     // Set Socket listener
    this.$socket.on('msg-input:' + this.id, function(msg)
    {
        vThis.doSomethingWith(msg);
    });
}

```

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [6 January 2026 18:40 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/10 "2026-01-06T18:40:32Z")

</div>

> [@omrid](#):
>
> using FireFox. It has stricter security, which seems to support `navigator.clipboard` only under HTTPS

This is the way it is going for everything. However, `http://localhost` should also be allowed. Not sure if FF does though. If Google have their way, all browser API's will require you to be in an HTTPS environment.

> [@gNeandr](#):
>
> Also with Brave, but that's also Firefox:

No, Brave uses Chromium, Same as Vivaldi, Edge, Chrome and many others.

> [@omrid](#):
>
> instead of using the `watch` mechanism (which has issues)

I would also say that if much of your UI requires `ui_templates`, you would be much better off not using D2 but rather UIBUILDER. With or without a framework (probably without).

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [6 January 2026 22:50 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/11 "2026-01-06T22:50:52Z")

</div>

_Just to show the copy2clip situation with a button to do the 'copy'_  
_on a ui-template and modal second ui-template_

On 1. **ui-template --\> copy to clip is OK**

```auto
<template>
    <v-btn color="primary" @click="copy2clip('test on 1.ui-template')">
      copy2clip
    </v-btn>
</template>

<script>
  methods: {
    cClip(ctext) {
        const dummy = document.createElement("textarea");
        dummy.value = `${ctext} \n ${new Date()}`;
        document.body.appendChild(dummy);
        dummy.select();
        try {
          document.execCommand('copy'); 
        } catch (err) {
          console.error("Copy failed !", err);
        }
   console.log(`copy2clip ${dummy.value}`)      
        document.body.removeChild(dummy);
    },
</script>

```

_The 1.ui-template has a msg call to a 2.ui-template which opens the **modal** window._  
_The second ui-template holds the same and code just to demonstrate the copy2clip.  
The text to be transferred is different._

```auto
<template>
    <v-btn color="primary" @click="copy2clip('TEST on 2.ui-template ???')">
        copy2clip
    </v-btn>
</template>

```

* * *

_Results_  
1.ui-template console.log

```auto
copy2clip test on 1.ui-template 
 Tue Jan 06 2026 23:23:09 GMT+0100 (Mitteleuropäische Normalzeit)

```

cntrl V to paste clip:

```auto
test on 1.ui-template 
 Tue Jan 06 2026 23:23:09 GMT+0100 (Mitteleuropäische Normalzeit)

```

2.ui-template console.log

```auto
copy2clip TEST on 2.ui-template ??? 
 Tue Jan 06 2026 23:23:45 GMT+0100 (Mitteleuropäische Normalzeit)

```

cntrl V to paste clip: (result is the clip content from 1.ui-template)

```auto
test on 1.ui-template 
 Tue Jan 06 2026 23:23:09 GMT+0100 (Mitteleuropäische Normalzeit)

```

* * *

_Point I don't understand:_  
With the 1.ui-template the copy to clip is **OK.**  
**The same code on a modal ui fails!**  
Any help very much welcomed ... or is the uibuilder the only way to go?

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [7 January 2026 16:18 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/12 "2026-01-07T16:18:34Z")

</div>

Here's a fully working example.  
[flows.json](https://discourse.nodered.org/uploads/short-url/b24O3zmHOogSWJBAKSdxLfZaWs9.json) (7.0 KB)

If on Firefox, you can replace the `navigator.clipboard' to the 'dummy element' thing

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [7 January 2026 17:15 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/13 "2026-01-07T17:15:45Z")

</div>

Even though the example works, it essentially corresponds to what works successfully in my previous post as 1.ui-template.  
So, the problem is not with the “first” ui-template, but when a second ui-template is opened as a **modal** by the first template and a copy to clip is made on this **modal window**.  
Then nothing happens, the browser (Firefox in my case) remains quiet, does not throw an error, does not copy anything to the clipboard. The exception is when I use navigator.clipboard. But that is a different problem.

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [7 January 2026 21:28 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/14 "2026-01-07T21:28:57Z")

</div>

Maybe I'm missing something here. Is the 2nd modal also a ui-template? (in my example it is a v-dialog, defined in and launched from within the 1st ui-template). Can you share the flow?

---

<div class="post-metadata">

**Author:** ![gNeandr](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gneandr/32/73947_2.png) [@gNeandr](https://discourse.nodered.org/u/gNeandr)\
**Post date:** [7 January 2026 22:52 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/15 "2026-01-07T22:52:22Z")

</div>

In principle, sharing the flow would not be a problem, but it is not suitable for posting in the forum. The internal JS/HTML structure is quite complex.  
My understanding so far is that the modal version prevents copy2clip here. Why? That is beyond my knowledge.  
As an alternative, I will now use a normal ui-template instead of the modal ui-template. Based on what we know so far, that should be no problem.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [6 February 2026 22:52 UTC](https://discourse.nodered.org/t/ui-template-copy-to-clip-using-watch-to-retrieve-other-node-data-as-well/100092/16 "2026-02-06T22:52:51Z")

</div>

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