From 4a0b5e6623bcd7ad2835c1aa5f25885cfbd7bb58 Mon Sep 17 00:00:00 2001
From: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com>
Date: Sun, 20 Sep 2020 23:21:00 +0000
Subject: [PATCH] Static props are now passed with wrap
---
Advanced Usage.md | 78 +++++++++++++++++----------------
README.md | 3 +-
Router.svelte | 14 +++---
test/app/src/App.svelte | 32 ++++----------
test/app/src/routes.js | 13 ++++++
test/app/src/routes/Foo.svelte | 10 +++++
test/app/src/routes/Home.svelte | 9 ----
test/cases/01-routing.test.js | 4 +-
wrap.js | 10 ++++-
9 files changed, 93 insertions(+), 80 deletions(-)
create mode 100644 test/app/src/routes/Foo.svelte
diff --git a/Advanced Usage.md b/Advanced Usage.md
index 3fddf5a..1a81800 100644
--- a/Advanced Usage.md
+++ b/Advanced Usage.md
@@ -8,10 +8,10 @@ Thanks to the many features of Svelte 3 or other components in the ecosystem, sv
- [Dynamically-imported routes and placeholders](#async-routes-and-loading-placeholders)
- [Route pre-conditions](#route-pre-conditions) ("route guards")
- [Adding user data to routes](#user-data)
+ - [Static props](#static-props)
- [`routeEvent` event](#routeevent-event)
- [`routeLoading` and `routeLoaded` events](#routeloading-and-routeloaded-events)
- [Querystring parsing](#querystring-parsing)
-- [Static props](#static-props)
- [Route transitions](#route-transitions)
- [Nested routers](#nested-routers)
- [Route groups](#route-groups)
@@ -26,6 +26,7 @@ The `wrap` method allows a few more interesting features, however:
- In addition to dynamically-importing components, you can define a component to be shown while a dynamically-imported one is being requested
- You can add pre-conditions to routes (sometimes called "route guards")
- You can add custom user data that is then used with the [`routeLoading` and `routeLoaded` events](#routeloading-and-routeloaded-events)
+- You can set static props, which are passed to the component as mounted by the router
### The `wrap` method
@@ -42,7 +43,8 @@ It accepts a single `options` argument that is an object with the following prop
- `options.loadingComponent`: Used together with `asyncComponent`, this is a Svelte component, that must be part of the bundle, which is displayed while `asyncComponent` is being downloaded. If this is empty, then the router will not display any component while the request is in progress.
- `options.loadingParams`: When using a `loadingComponent`, this is an optional dictionary that will be passed to the component as the `params` prop.
- `options.userData`: Optional dictionary that will be passed to events such as `routeLoading`, `routeLoaded`, `conditionsFailed`.
-- `options.conditions`: Array of route pre-condition functions to add, which will be executed in order.
+- `options.conditions`: Optional array of route pre-condition functions to add, which will be executed in order.
+- `options.props`: Optional dictionary of props that are passed to the component when mounted. The props are expanded with the spread operator (`{...props}`), so the key of each element becomes the name of the prop.
One and only one of `options.component` or `options.asyncComponent` must be set; all other properties are optional.
@@ -269,6 +271,43 @@ function routeLoaded(event) {
````
+### Static props
+
+In certain cases, you might need to pass static props to a component within the router.
+
+For example, assume this component `Foo.svelte`:
+
+```svelte
+
The secret number is {num}
+
+```
+
+If `Foo` is a route in your application, you can pass a series of props to it through the router, using `wrap`:
+
+```svelte
+
+
+```
+
## `routeEvent` event
The custom `routeEvent` event can be used to bubble events from a component displayed by the router, to the router's parent component.
@@ -427,41 +466,6 @@ With the same URL as before, the result would be:
qs supports advanced things such as arrays, nested objects, etc. Check out their [README](https://github.com/ljharb/qs) for more information.
-## Static props
-
-In certain cases, you might need to pass static props to a component within the router.
-
-For example, assume this component `Foo.svelte`:
-
-```svelte
-The secret number is {num}
-
-```
-
-If `Foo` is a route in your application, you can pass a series of props to it through the router, with:
-
-```svelte
-
-
-```
-
## Route transitions
It's easy to add a nice transition between routes, leveraging the built-in [transitions](https://svelte.dev/docs#Transitions) of Svelte 3.
diff --git a/README.md b/README.md
index 29635d1..3a43b6a 100644
--- a/README.md
+++ b/README.md
@@ -176,7 +176,7 @@ const routes = {
}
````
-The `wrap` method accepts an object with multiple properties and enables other features, including: setting a "loading" component that is shown while a dynamically-imported component is being requested, adding custom user data, adding pre-conditions (route guards), etc.
+The `wrap` method accepts an object with multiple properties and enables other features, including: setting a "loading" component that is shown while a dynamically-imported component is being requested, adding pre-conditions (route guards), passing static props, and adding custom user data.
You can learn more about all the features of `wrap` in the documentation for [route wrapping](/Advanced%20Usage.md#route-wrapping).
@@ -369,6 +369,7 @@ Check out the [Advanced Usage](/Advanced%20Usage.md) documentation for using:
- [Dynamically-imported routes and placeholders](/Advanced%20Usage.md#async-routes-and-loading-placeholders)
- [Route pre-conditions](/Advanced%20Usage.md#route-pre-conditions) ("route guards")
- [Adding user data to routes](/Advanced%20Usage.md#user-data)
+ - [Static props](/Advanced%20Usage.md#static-props)
- [`routeEvent` event](/Advanced%20Usage.md#routeevent-event)
- [`routeLoading` and `routeLoaded` events](/Advanced%20Usage.md#routeloading-and-routeloaded-events)
- [Querystring parsing](/Advanced%20Usage.md#querystring-parsing)
diff --git a/Router.svelte b/Router.svelte
index 27184a2..4dc8351 100644
--- a/Router.svelte
+++ b/Router.svelte
@@ -246,11 +246,6 @@ export let prefix = ''
*/
export let restoreScrollState = false
-/**
- * Optional props that are passed to each component in the router, expanded (with `{...props}`)
- */
-export let props = {}
-
/**
* Container for a route: path, component
*/
@@ -259,7 +254,7 @@ class RouteItem {
* Initializes the object and creates a regular expression from the path, using regexparam.
*
* @param {string} path - Path to the route (must start with '/' or '*')
- * @param {SvelteComponent} component - Svelte component for the route
+ * @param {SvelteComponent|WrappedComponent} component - Svelte component for the route, optionally wrapped
*/
constructor(path, component) {
if (!component || (typeof component != 'function' && (typeof component != 'object' || component._sveltesparouter !== true))) {
@@ -283,11 +278,13 @@ class RouteItem {
this.component = component.component
this.conditions = component.conditions || []
this.userData = component.userData
+ this.props = component.props || {}
}
else {
// Convert the component to a function that returns a Promise, to normalize it
this.component = () => Promise.resolve(component)
this.conditions = []
+ this.props = {}
}
this._pattern = pattern
@@ -388,6 +385,7 @@ else {
// Props for the component to render
let component = null
let componentParams = null
+let props = {}
// Event dispatcher from Svelte
const dispatch = createEventDispatcher()
@@ -467,6 +465,7 @@ loc.subscribe(async (newLoc) => {
if (obj.loading) {
component = obj.loading
componentParams = obj.loadingParams
+ props = {}
// Trigger the routeLoaded event for the loading component
// Create a copy of detail so we don't modify the object for the dynamic route (and the dynamic route doesn't modify our object too)
@@ -504,6 +503,9 @@ loc.subscribe(async (newLoc) => {
componentParams = null
}
+ // Set static props, if any
+ props = routesList[i].props
+
dispatchNextTick('routeLoaded', detail)
break
}
diff --git a/test/app/src/App.svelte b/test/app/src/App.svelte
index 9435399..b4bef74 100644
--- a/test/app/src/App.svelte
+++ b/test/app/src/App.svelte
@@ -26,26 +26,14 @@
-{#if staticProps}
-
-{:else}
-
-{/if}
+
Dynamic links
@@ -127,10 +115,6 @@ function routeEvent(event) {
const urlParams = new URLSearchParams(window.location.search)
const restoreScrollState = !!urlParams.has('scroll')
-// Check if we need to set static props, which are enabled by setting the "props=1" querystring parameter
-// We're checking this for the tests, but in your code you will likely want to set this value manually
-const staticProps = urlParams.has('props') ? {staticProp: 'foo'} : null
-
// List of dynamic links
let dynamicLinks = [
{
diff --git a/test/app/src/routes.js b/test/app/src/routes.js
index 9c6ce44..4e2c816 100644
--- a/test/app/src/routes.js
+++ b/test/app/src/routes.js
@@ -10,6 +10,7 @@ import Home from './routes/Home.svelte'
import Name from './routes/Name.svelte'
import Loading from './routes/Loading.svelte'
import Wild from './routes/Wild.svelte'
+import Foo from './routes/Foo.svelte'
import Regex from './routes/Regex.svelte'
import NotFound from './routes/NotFound.svelte'
@@ -96,6 +97,12 @@ if (!urlParams.has('routemap')) {
// The second argument is a custom data object that will be passed to the `conditionsFailed` event if the pre-conditions fail
'/lucky': wrappedLuckyRoute,
+ // This route has a static prop that is passed to it
+ '/foo': wrap({
+ component: Foo,
+ props: {staticProp: 'this is static'}
+ }),
+
// This component contains a nested router
// Note that we must match both '/nested' and '/nested/*' for the nested router to work (or look below at doing this with a Map and a regular expression)
'/nested': wrap({
@@ -134,6 +141,12 @@ else {
// The second argument is a custom data object that will be passed to the `conditionsFailed` event if the pre-conditions fail
routes.set('/lucky', wrappedLuckyRoute)
+ // This route has a static prop that is passed to it
+ routes.set('/foo', wrap({
+ component: Foo,
+ props: {staticProp: 'this is static'}
+ }))
+
// Regular expressions
routes.set(/^\/regex\/(.*)?/i, Regex)
routes.set(/^\/(pattern|match)(\/[a-z0-9]+)?/i, Regex)
diff --git a/test/app/src/routes/Foo.svelte b/test/app/src/routes/Foo.svelte
new file mode 100644
index 0000000..16a186b
--- /dev/null
+++ b/test/app/src/routes/Foo.svelte
@@ -0,0 +1,10 @@
+{#if staticProp}
+ We have a static prop: {staticProp}
+{:else}
+ No static props here!
+{/if}
+
+
\ No newline at end of file
diff --git a/test/app/src/routes/Home.svelte b/test/app/src/routes/Home.svelte
index a014fc5..a28e6d8 100644
--- a/test/app/src/routes/Home.svelte
+++ b/test/app/src/routes/Home.svelte
@@ -9,12 +9,3 @@
Manually change the URL's fragment/hash
Try refreshing the page
-
-{#if staticProp}
- We have a static prop: {staticProp}
-{/if}
-
-
diff --git a/test/cases/01-routing.test.js b/test/cases/01-routing.test.js
index 6dafa97..8a458af 100644
--- a/test/cases/01-routing.test.js
+++ b/test/cases/01-routing.test.js
@@ -370,9 +370,9 @@ describe(' component', function() {
it('static props', (browser) => {
browser
- .url(browser.launchUrl + '?props=1')
+ .url(browser.launchUrl + '/#/foo')
.waitForElementVisible('#staticprop')
- .expect.element('#staticprop').text.to.equal('foo')
+ .expect.element('#staticprop').text.to.equal('this is static')
browser.end()
})
diff --git a/wrap.js b/wrap.js
index e031ae4..4741a8b 100644
--- a/wrap.js
+++ b/wrap.js
@@ -1,5 +1,10 @@
/**
* @typedef {Object} WrappedComponent
+ * @property {SvelteComponent} component - Component to load (this is always asynchronous)
+ * @property {RoutePrecondition[]} [conditions] - Route pre-conditions to validate
+ * @property {Object} [props] - Optional dictionary of static props
+ * @property {Object} [userData] - Optional user data dictionary
+ * @property {bool} _sveltesparouter - Internal flag; always set to true
*/
/**
@@ -15,6 +20,7 @@
* @property {SvelteComponent} [loadingComponent] - Svelte component to be displayed while the async route is loading (as a placeholder); when unset or false-y, no component is shown while component
* @property {Object} [loadingParams] - Optional dictionary passed to the `loadingComponent` component as params (for an exported prop called `params`)
* @property {Object} [userData] - Optional object that will be passed to events such as `routeLoading`, `routeLoaded`, `conditionsFailed`
+ * @property {Object} [props] - Optional key-value dictionary of static props that will be passed to the component. The props are expanded with {...props}, so the key in the dictionary becomes the name of the prop.
* @property {RoutePrecondition[]|RoutePrecondition} [conditions] - Route pre-conditions to add, which will be executed in order
*/
@@ -22,7 +28,8 @@
* Wraps a component to enable multiple capabilities:
* 1. Using dynamically-imported component, with (e.g. `{asyncComponent: () => import('Foo.svelte')}`), which also allows bundlers to do code-splitting.
* 2. Adding route pre-conditions (e.g. `{conditions: [...]}`)
- * 3. Adding custom userData, which is passed to route events (e.g. route loaded events) or to route pre-conditions (e.g. `{userData: {foo: 'bar}}`)
+ * 3. Adding static props that are passed to the component
+ * 4. Adding custom userData, which is passed to route events (e.g. route loaded events) or to route pre-conditions (e.g. `{userData: {foo: 'bar}}`)
*
* @param {WrapOptions} args - Arguments object
* @returns {WrappedComponent} Wrapped component
@@ -71,6 +78,7 @@ export function wrap(args) {
component: args.asyncComponent,
userData: args.userData,
conditions: (args.conditions && args.conditions.length) ? args.conditions : undefined,
+ props: (args.props && Object.keys(args.props).length) ? args.props : {},
_sveltesparouter: true
}