Skip to content

Commit f946607

Browse files
johnjenkinsJohn Jenkins
andauthored
feat: 4.38 release (#1549)
* feat: 4.38 release * chore: usa * chore: more tweaks --------- Co-authored-by: John Jenkins <john.jenkins@nanoporetech.com>
1 parent da3fea3 commit f946607

File tree

108 files changed

+16819
-14
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+16819
-14
lines changed

docs/components/api.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,17 +217,17 @@ The following primitives can be imported from the `@stencil/core` package and us
217217
```
218218
__Example:__
219219
```ts
220-
import { Mixin, Component, h, Prop, State } from '@stencil/core'
220+
import { Mixin, MixinFactory, Component, h, Prop, State } from '@stencil/core'
221221

222-
const aFactory = (Base) => {
223-
class A extends Base { private propA = 'A' };
222+
const aFactory: MixinFactory = (Base) => {
223+
class A extends Base { propA = 'A' };
224224
return A;
225225
}
226-
const bFactory = (Base) => {
226+
const bFactory: MixinFactory = (Base) => {
227227
class B extends Base { @Prop() propB = 'B' };
228228
return B;
229229
}
230-
const cFactory = (Base) => {
230+
const cFactory: MixinFactory = (Base) => {
231231
class C extends Base { @State() propC = 'C' };
232232
return C;
233233
}

docs/components/properties.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,11 +1018,3 @@ export class GetSetProps {
10181018

10191019
Most of the documentation referring to [types](#types) and [options](#prop-options) also apply to get / set `@Prop`s
10201020
(with the exception of [mutable](#prop-mutability-mutable) as this makes little logical sense).
1021-
1022-
:::note
1023-
When using the [dist / 'lazy' output target](../output-targets/dist.md), a known limitation is if an initial value
1024-
is set (via an attribute e.g. `<my-cmp attr="initial">`) in-order to 'hit' the set() method, Stencil can only do so
1025-
*after* lazily fetching / loading the class component. This means one initial render / tick before being able to set the incoming, initial value.
1026-
1027-
This limitation does not exist on the [custom-elements output-target](../output-targets/custom-elements.md).
1028-
:::
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
title: Serialization & Deserialization
3+
sidebar_label: Serialization & Deserialization
4+
description: Serialization & Deserialization
5+
slug: /serialization
6+
---
7+
8+
# Serialization & Deserialization
9+
10+
Custom elements interact with the DOM either via HTML [attributes](https://open-wc.org/guides/knowledge/attributes-and-properties/#attributes) (always strings) or JavaScript [properties](https://open-wc.org/guides/knowledge/attributes-and-properties/#properties). Stencil automatically tries to keep properties and attributes in-sync when possible via **serialization** (turning properties into strings) and **deserialization** (turning strings back into properties).
11+
12+
For example, if you have a component defined like this:
13+
14+
```tsx
15+
@Component({
16+
tag: 'my-component',
17+
})
18+
export class MyComponent {
19+
// Stencil 'sees' this as a number type.
20+
// Numbers are easy to convert to/from strings
21+
@Prop({ reflect: true }) myNumber: number;
22+
}
23+
```
24+
25+
When the property is set via JavaScript:
26+
27+
```js
28+
const myComponent = document.querySelector('my-component');
29+
myComponent.myNumber = 42;
30+
```
31+
32+
Stencil will automatically serialize `myNumber` to an attribute:
33+
34+
```html
35+
<my-component my-number="42"></my-component>
36+
```
37+
38+
Conversely, if the attribute is set in HTML:
39+
40+
```html
41+
<!-- in html -->
42+
<my-component my-number="42"></my-component>
43+
<!-- or js -->
44+
<script>
45+
const myComponent = document.querySelector('my-component');
46+
myComponent.setAttribute('my-number', '43');
47+
</script>
48+
```
49+
50+
Stencil will automatically deserialize the attribute back to the property:
51+
52+
```js
53+
console.log(myComponent.myNumber); // 43
54+
```
55+
56+
Most of the time Stencil's automatic serialization and deserialization is enough - especially with primitive data types, however there are cases where you might want to customize this behavior, especially when dealing with complex data.
57+
58+
59+
## The PropSerializer Decorator (`@PropSerializer()`)
60+
61+
The `@PropSerializer()` decorator allows you to define custom serialization logic; converting a JavaScript property to a attribute string. The decorator accepts a single argument; the name of the class member `@Prop()` it is associated with. A method decorated with `@PropSerializer()` will automatically run when its associated property changes.
62+
63+
```tsx
64+
import { Component, Prop, PropSerializer } from '@stencil/core';
65+
66+
@Component({
67+
tag: 'my-component',
68+
})
69+
export class MyComponent {
70+
@Prop() aStringArray: string[];
71+
72+
@PropSerializer('aStringArray')
73+
serializeStringArray(value: string[]) {
74+
try {
75+
return JSON.stringify(value); // must return a string
76+
} catch (e) {
77+
return null; // returning null removes the attribute
78+
}
79+
}
80+
}
81+
```
82+
83+
In the example above, the `serializeStringArray` method will run whenever the `aStringArray` property changes - the returned value will be used to update the attribute (no need to set `{reflect: true}` on the `@Prop()` decorator). E.g.
84+
85+
```js
86+
const myComponent = document.querySelector('my-component');
87+
myComponent.aStringArray = ['Hello', 'World'];
88+
```
89+
90+
Becomes:
91+
92+
```html
93+
<my-component a-string-array='["Hello","World"]'></my-component>
94+
```
95+
96+
## The AttrDeserializer Decorator (`@AttrDeserializer()`)
97+
98+
The `@AttrDeserializer()` decorator allows you to define custom deserialization logic; converting an attribute string to a JavaScript property. The decorator accepts a single argument; the name of the class member `@Prop()` it is associated with. A method decorated with `@AttrDeserializer()` will automatically run when its associated attribute changes.
99+
100+
```tsx
101+
import { Component, Prop, AttrDeserializer } from '@stencil/core';
102+
103+
@Component({
104+
tag: 'my-component',
105+
})
106+
export class MyComponent {
107+
@Prop() aStringArray: string[];
108+
109+
@AttrDeserializer('aStringArray')
110+
deserializeStringArray(value: string): string[] | null {
111+
try {
112+
return JSON.parse(value);
113+
} catch (e) {
114+
return null;
115+
}
116+
}
117+
}
118+
```
119+
120+
In the example above, the `deserializeStringArray` method will run whenever the `a-string-array` attribute changes. The method takes the new value of the attribute as an argument and must return the deserialized value.
121+
122+
Now, when you set the attribute in HTML:
123+
124+
```html
125+
<my-component a-string-array='["Hello","World"]'></my-component>
126+
```
127+
128+
Stencil will automatically deserialize the attribute back to the property:
129+
130+
```js
131+
const myComponent = document.querySelector('my-component');
132+
console.log(myComponent.aStringArray); // ['Hello', 'World']
133+
```
134+
135+
## Practical uses of PropSerializer
136+
137+
Practically speaking, there is little disadvantage in using a `@AttrDeserializer()` on a complex property; it just adds another method for users to provide data to your component.
138+
139+
The use-cases around using `@PropSerializer()` is slightly less obvious as in general, [it is not considered best practice to reflect complex data (like objects or arrays) as attributes](https://web.dev/articles/custom-elements-best-practices#aim-to-only-accept-rich-data-objects,-arrays-as-properties.)
140+
141+
The following example illustrates a practical use case for `@PropSerializer()` using the [hydrate script output](../guides/hydrate-app.md) on a server we can fetch and serialize complex data to an attribute. When the same component loads in a browser, the component can de-serialize the data immediately without having to do another fetch.
142+
143+
```tsx
144+
import { AttrDeserialize, Build, Component, h, Prop, PropSerialize } from '@stencil/core';
145+
146+
interface User {
147+
userName: string;
148+
avatarUrl: string;
149+
posts: any[]
150+
}
151+
152+
@Component({
153+
tag: 'user-login-panel',
154+
})
155+
export class UserLogin {
156+
@Prop() user: User;
157+
158+
// On the server *only* let's represent the user's data as an attribute
159+
// this allows the browser to get the data immediately without having to do a client-side fetch
160+
161+
@PropSerialize('user')
162+
userSerialize(newVal: User) {
163+
if (Build.isBrowser) {
164+
return null;
165+
}
166+
try { return JSON.stringify(newVal); }
167+
catch (e) { return null; }
168+
}
169+
170+
// Whenever we have an attribute (including on client init)
171+
// let's turn it back into an object that we can use and render
172+
173+
@AttrDeserialize('user')
174+
userDeserialize(newVal: string) {
175+
try { return JSON.parse(newVal); }
176+
catch (e) { return null; }
177+
}
178+
179+
async componentWillLoad() {
180+
181+
// On the server *only*, let's do a secret login involving private keys etc.
182+
183+
if (Build.isServer) {
184+
// Because we have a serializer method,
185+
// setting a value automatically reflects it to the dom attribute
186+
187+
this.user = login(credentials);
188+
}
189+
}
190+
191+
render() {
192+
if (this.user) return (`Welcome ${this.user.userName}!`);
193+
else return (`Please login`);
194+
}
195+
}
196+
```

versioned_docs/version-v4.37/components/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ The following primitives can be imported from the `@stencil/core` package and us
220220
import { Mixin, Component, h, Prop, State } from '@stencil/core'
221221

222222
const aFactory = (Base) => {
223-
class A extends Base { private propA = 'A' };
223+
class A extends Base { propA = 'A' };
224224
return A;
225225
}
226226
const bFactory = (Base) => {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: Build Constants
3+
description: Stencil has a number of add-ons that you can use with the build process.
4+
slug: /build-variables
5+
---
6+
7+
# Build Constants
8+
9+
Build Constants in Stencil allow you to run specific code only when Stencil is running in development mode. This code is stripped from your bundles when doing a production build, therefore keeping your bundles as small as possible.
10+
11+
### Using Build Constants
12+
13+
Lets dive in and look at an example of how to use our build constants:
14+
15+
```tsx
16+
import { Component, Build } from '@stencil/core';
17+
18+
@Component({
19+
tag: 'stencil-app',
20+
styleUrl: 'stencil-app.scss'
21+
})
22+
export class StencilApp {
23+
24+
componentDidLoad() {
25+
if (Build.isDev) {
26+
console.log('im in dev mode');
27+
} else {
28+
console.log('im running in production');
29+
}
30+
31+
if (Build.isBrowser) {
32+
console.log('im in the browser');
33+
} else {
34+
console.log('im in prerendering (server)');
35+
}
36+
}
37+
}
38+
```
39+
40+
As you can see from this example, we just need to import `Build` from `@stencil/core` and then we can use the `isDev` constant to detect when we are running in dev mode or production mode.
41+
42+
### Use Cases
43+
44+
Some use cases we have come up with are:
45+
46+
- Diagnostics code that runs in dev to make sure logic is working like you would expect
47+
- `console.log()`'s that may be useful for debugging in dev mode but that you don't want to ship
48+
- Disabling auth checks when in dev mode
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"label": "Components",
3+
"position": 2
4+
}

0 commit comments

Comments
 (0)