Repository: holoviz/holoviews

Issue:
Streams not working with overlays when using bind or streams dict

This works when no overlay is involved:

```python
tap = hv.streams.Tap()
def test(x, y):
    return hv.VLine(x or 0)
dmap = hv.DynamicMap(pn.bind(test, x=tap.param.x, y=tap.param.y))
hv.Curve([1, 2, 3])  * dmap
```

![image](https://user-images.githubusercontent.com/890576/119846584-e1c19500-becf-11eb-9e86-0bd06f8e3c32.png)

But introducing an overlay breaks it:

```python
tap = hv.streams.Tap()
def test(x, y):
    return hv.Curve([1, 2, 3]) * hv.VLine(x or 0)
dmap = hv.DynamicMap(pn.bind(test, x=tap.param.x, y=tap.param.y))
dmap
```

Using the streams dictionary has the same issue:

```python
tap = hv.streams.Tap()
def test(x, y):
    return hv.Curve([1, 2, 3]) * hv.VLine(x or 0)
dmap = hv.DynamicMap(test, streams=dict(x=tap.param.x, y=tap.param.y))
dmap
```

The old streams list approach is also still working.

```python
>>> param.__version__, bokeh.__version__, hv.__version__
('1.10.1', '2.3.1', '1.14.4')
```


Discussion:

Resolving patch:
diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py
--- a/holoviews/plotting/util.py
+++ b/holoviews/plotting/util.py
@@ -21,7 +21,7 @@
     closest_match, is_number, isfinite, python2sort, disable_constant,
     arraylike_types
 )
-from ..streams import LinkedStream
+from ..streams import LinkedStream, Params
 from ..util.transform import dim
 
 
@@ -183,7 +183,8 @@ def compute_overlayable_zorders(obj, path=[]):
     # If object branches but does not declare inputs (e.g. user defined
     # DynamicMaps returning (Nd)Overlay) add the items on the DynamicMap.last
     found = any(isinstance(p, DynamicMap) and p.callback._is_overlay for p in path)
-    linked =  any(isinstance(s, LinkedStream) and s.linked for s in obj.streams)
+    linked =  any(isinstance(s, (LinkedStream, Params)) and s.linked
+                  for s in obj.streams)
     if (found or linked) and isoverlay and not isdynoverlay:
         offset = max(zorder_map.keys())
         for z, o in enumerate(obj.last):
diff --git a/holoviews/streams.py b/holoviews/streams.py
--- a/holoviews/streams.py
+++ b/holoviews/streams.py
@@ -694,6 +694,11 @@ def __init__(self, parameterized=None, parameters=None, watch=True, watch_only=F
                     rename.update({(o, k): v for o in owners})
             params['rename'] = rename
 
+        if 'linked' not in params:
+            for p in parameters:
+                if isinstance(p.owner, (LinkedStream, Params)) and p.owner.linked:
+                    params['linked'] = True
+
         self._watch_only = watch_only
         super().__init__(parameterized=parameterized, parameters=parameters, **params)
         self._memoize_counter = 0
@@ -713,7 +718,6 @@ def unwatch(self):
             watcher.inst.param.unwatch(watcher)
         self._watchers.clear()
 
-
     @classmethod
     def from_params(cls, params, **kwargs):
         """Returns Params streams given a dictionary of parameters


Test patch:
diff --git a/holoviews/tests/plotting/bokeh/test_overlayplot.py b/holoviews/tests/plotting/bokeh/test_overlayplot.py
--- a/holoviews/tests/plotting/bokeh/test_overlayplot.py
+++ b/holoviews/tests/plotting/bokeh/test_overlayplot.py
@@ -1,9 +1,10 @@
 import numpy as np
+import panel as pn
 
 from holoviews.core import NdOverlay, HoloMap, DynamicMap, Overlay
 from holoviews.core.options import Cycle
 from holoviews.element import Curve, Points, ErrorBars, Scatter, Text, VLine
-from holoviews.streams import Stream
+from holoviews.streams import Stream, Tap
 from holoviews.util import Dynamic
 
 from ...utils import LoggingComparisonTestCase
@@ -240,6 +241,29 @@ def test_overlay_muted_renderer(self):
         self.assertFalse(unmuted.handles['glyph_renderer'].muted)
         self.assertTrue(muted.handles['glyph_renderer'].muted)
 
+    def test_overlay_params_bind_linked_stream(self):
+        tap = Tap()
+        def test(x):
+            return Curve([1, 2, 3]) * VLine(x or 0)
+        dmap = DynamicMap(pn.bind(test, x=tap.param.x))
+        plot = bokeh_renderer.get_plot(dmap)
+
+        tap.event(x=1)
+        _, vline_plot = plot.subplots.values()
+        assert vline_plot.handles['glyph'].location == 1
+
+    def test_overlay_params_dict_linked_stream(self):
+        tap = Tap()
+        def test(x):
+            return Curve([1, 2, 3]) * VLine(x or 0)
+        dmap = DynamicMap(test, streams={'x': tap.param.x})
+        plot = bokeh_renderer.get_plot(dmap)
+
+        tap.event(x=1)
+        _, vline_plot = plot.subplots.values()
+        assert vline_plot.handles['glyph'].location == 1
+
+        
 
 class TestLegends(TestBokehPlot):
 
@@ -315,7 +339,6 @@ def test_dynamicmap_legend_updates_add_dynamic_plots(self):
         plot.update((3,))
         legend_labels = [item.label for item in plot.state.legend[0].items]
         self.assertEqual(legend_labels, [{'value': 'A'}, {'value': 'B'}, {'value': 'C'}])
-
     def test_dynamicmap_ndoverlay_shrink_number_of_items(self):
         selected = Stream.define('selected', items=3)()
         def callback(items):