@mschop/alpine-web-components 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AlpineWebComponent.ts +13 -2
- package/README.md +29 -0
- package/package.json +1 -1
package/AlpineWebComponent.ts
CHANGED
|
@@ -81,7 +81,7 @@ abstract class AlpineWebComponent extends HTMLElement {
|
|
|
81
81
|
let state = this.state;
|
|
82
82
|
|
|
83
83
|
// @ts-ignore
|
|
84
|
-
state.component = this;
|
|
84
|
+
state.component = this.markRaw(this);
|
|
85
85
|
|
|
86
86
|
if (typeof state.attributes === 'undefined') {
|
|
87
87
|
state.attributes = {};
|
|
@@ -92,6 +92,17 @@ abstract class AlpineWebComponent extends HTMLElement {
|
|
|
92
92
|
this.state = Alpine.reactive(state);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
protected markRaw<T>(value: T): T {
|
|
96
|
+
if (value !== null && typeof value === 'object') {
|
|
97
|
+
Object.defineProperty(value, '__v_skip', {
|
|
98
|
+
value: true,
|
|
99
|
+
enumerable: false,
|
|
100
|
+
configurable: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
95
106
|
protected bootAlpine() {
|
|
96
107
|
// @ts-ignore
|
|
97
108
|
Alpine.addScopeToNode(this.getRoot(), this.state)
|
|
@@ -163,4 +174,4 @@ document.addEventListener('alpine:initialized', () => {
|
|
|
163
174
|
AlpineWebComponent.isAlpineInitStarted = true
|
|
164
175
|
})
|
|
165
176
|
|
|
166
|
-
export default AlpineWebComponent
|
|
177
|
+
export default AlpineWebComponent
|
package/README.md
CHANGED
|
@@ -47,4 +47,33 @@ window.customElements.define('my-counter', Counter);
|
|
|
47
47
|
|
|
48
48
|
You should now be able to reference the counter with `<my-counter></my-counter>`
|
|
49
49
|
|
|
50
|
+
The component instance is attached as `state.component` but marked raw, so Alpine won't proxy the full custom element. You can call component methods from the Alpine scope:
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<button @click="component.resetCounter()">Reset</button>
|
|
54
|
+
```
|
|
55
|
+
|
|
50
56
|
Please see directory `example` for example on how to bind / update attributes etc..
|
|
57
|
+
|
|
58
|
+
## Attribute-based state (string-only, isolated)
|
|
59
|
+
|
|
60
|
+
The default behavior mirrors component state to HTML attributes (and reads attributes back into state). This keeps the component interface purely declarative and works well for primitives (string/number/boolean).
|
|
61
|
+
|
|
62
|
+
Key points:
|
|
63
|
+
- Attributes are always strings, so complex data must be serialized (e.g. JSON) via `attributeCasts`.
|
|
64
|
+
- When state changes, the attribute is updated and an `updated-<key>` event is emitted.
|
|
65
|
+
- When an attribute changes externally, the component state is updated.
|
|
66
|
+
|
|
67
|
+
This approach is useful when you want a clean HTML-only API and strict isolation between components. For complex data, consider property binding with `x-effect` (see section above).
|
|
68
|
+
|
|
69
|
+
## Passing complex data (avoid JSON in attributes)
|
|
70
|
+
|
|
71
|
+
Attributes are strings, so passing objects requires JSON encoding/decoding. If you want to share complex data (objects, arrays, reactive proxies) between components, bind a **property** instead of an attribute.
|
|
72
|
+
|
|
73
|
+
Alpine's `x-effect` runs once on init and re-runs when dependencies change, so you can declaratively sync a property without serialization:
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<my-child x-effect="$el.state.shared = parentState"></my-child>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This keeps the template declarative while passing the actual object reference. Use attributes for primitives and properties for complex data.
|