Repository: holoviz/panel

Issue:
Location sync broken for dates, ints
#### ALL software version info

```
Python 3.9.0

bokeh==2.2.3
notebook==5.7.9
panel==0.10.1

macOS Catalina 10.15.7

Chrome 85.0.4183.121
```

#### Description of expected behavior and the observed behavior

##### Expected

When syncing params with the browser location, refreshing the page or sharing the URL should restore those param values.

##### Observed

Refreshing the page or sharing the URL does not restore param values for dates and ints. It does seem to work for strings and floats.

Editing the URL by changing `?bday="2000-01-01"` to `?bday=2000-01-01` lets you share the URL (though the sync on page load immediately changes the URL back).

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import panel as pn
import panel.widgets as pnw

bday = pnw.DatePicker()
pn.state.location.sync(bday, {"value": "bday"})

@pn.depends(bday)
def summary(bday):
    return f"My birthday is {bday}"

app = pn.Column(bday, summary)
app.servable()
```

#### Stack traceback and/or browser JavaScript console output

```
2020-11-13 16:12:06,453 Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x142be9c10>>, <Task finished name='Task-369' coro=<_needs_document_lock.<locals>._needs_document_lock_wrapper() done, defined at /Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/bokeh/server/session.py:51> exception=ValueError("CalendarDate 'value' only takes datetime types.")>)
Traceback (most recent call last):
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/tornado/ioloop.py", line 741, in _run_callback
    ret = callback()
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
    future.result()
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/bokeh/server/session.py", line 71, in _needs_document_lock_wrapper
    result = await result
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/tornado/gen.py", line 216, in wrapper
    result = ctx_run(func, *args, **kwargs)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/panel/reactive.py", line 194, in _change_coroutine
    self._change_event(doc)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/panel/reactive.py", line 204, in _change_event
    self._process_events(events)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/panel/reactive.py", line 187, in _process_events
    self.param.set_param(**self._process_property_change(events))
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 1451, in set_param
    self_._batch_call_watchers()
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 1578, in _batch_call_watchers
    watcher.fn(*events)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/panel/io/location.py", line 108, in _update_synced
    p.param.set_param(**mapped)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 1444, in set_param
    setattr(self_or_cls, k, v)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 302, in _f
    instance_param.__set__(obj, val)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 304, in _f
    return f(self, obj, val)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/__init__.py", line 623, in __set__
    super(Dynamic,self).__set__(obj,val)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 304, in _f
    return f(self, obj, val)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/parameterized.py", line 871, in __set__
    self._validate(val)
  File "/Users/johnzeringue/.pyenv/versions/3.9.0/envs/pydata-panel/lib/python3.9/site-packages/param/__init__.py", line 1891, in _validate
    raise ValueError("CalendarDate '%s' only takes datetime types."%self.name)
ValueError: CalendarDate 'value' only takes datetime types.
```

#### Screenshots or screencasts of the bug in action

<img width="478" alt="Screen Shot 2020-11-13 at 4 24 00 PM" src="https://user-images.githubusercontent.com/3858785/99122635-b844ba00-25cc-11eb-83c4-57760c138586.png">



Discussion:

Resolving patch:
diff --git a/panel/io/location.py b/panel/io/location.py
--- a/panel/io/location.py
+++ b/panel/io/location.py
@@ -2,6 +2,7 @@
 Defines the Location  widget which allows changing the href of the window.
 """
 
+import json
 import urllib.parse as urlparse
 
 import param
@@ -89,33 +90,32 @@ def _update_synced(self, event=None):
                 if k not in mapping:
                     continue
                 pname = mapping[k]
-                if isinstance(v, str) and v.startswith('"') and v.endswith('"'):
-                    v = v[1:-1]
-                else:
-                    try:
-                        v = p.param[pname].deserialize(v)
-                    except Exception:
-                        pass
+                try:
+                    v = p.param[pname].deserialize(v)
+                except Exception:
+                    pass
                 mapped[pname] = v
             p.param.set_param(**mapped)
 
     def _update_query(self, *events, query=None):
         if self._syncing:
             return
-        query = query or {}
+        serialized = query or {}
         for e in events:
             matches = [ps for o, ps, _ in self._synced if o in (e.cls, e.obj)]
             if not matches:
                 continue
             owner = e.cls if e.obj is None else e.obj
             try:
-                val = owner.param.serialize_value(e.name)
+                val = owner.param[e.name].serialize(e.new)
             except Exception:
                 val = e.new
-            query[matches[0][e.name]] = val
+            if not isinstance(val, str):
+                val = json.dumps(val)
+            serialized[matches[0][e.name]] = val
         self._syncing = True
         try:
-            self.update_query(**{k: v for k, v in query.items() if v is not None})
+            self.update_query(**{k: v for k, v in serialized.items() if v is not None})
         finally:
             self._syncing = False
 
@@ -150,8 +150,19 @@ def sync(self, parameterized, parameters=None):
         watcher = parameterized.param.watch(self._update_query, list(parameters))
         self._synced.append((parameterized, parameters, watcher))
         self._update_synced()
-        self._update_query(query={v: getattr(parameterized, k)
-                                  for k, v in parameters.items()})
+        query = {}
+        for p, name in parameters.items():
+            v = getattr(parameterized, p)
+            if v is None:
+                continue
+            try:
+                parameterized.param[p].serialize(v)
+            except Exception:
+                pass
+            if not isinstance(v, str):
+                v = json.dumps(v)
+            query[name] = v
+        self._update_query(query=query)
 
     def unsync(self, parameterized, parameters=None):
         """


Test patch: