Skip to content

Commit

Permalink
resolve data binding for not defined elements (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
mahmoudmoravej authored Dec 3, 2024
1 parent 74b5d0f commit 1154b06
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-mails-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workleap/r2wc": patch
---

Fix a bug: Resolve web elements "data" properties even if they have been set sooner than the element gets defined.
30 changes: 29 additions & 1 deletion packages/r2wc/src/WebComponentHTMLElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,35 @@ export class WebComponentHTMLElementBase extends HTMLElement {
}

export class WebComponentHTMLElement<Props= unknown, ObservedAttributesType extends string = never> extends WebComponentHTMLElementBase {
#props = new Observable<Props>();
#props : Observable<Props>;

constructor() {
super();

let alreadySetData: Props | undefined = undefined;

try {
//if the data is set before the element is defined in the DOM, the getter function will not override it!
//so, we read the data to initialize the props with it and then remove it.
//Removing it causes the getter to work as expected afterwards.
//There are different reasons that causes the element to be defined in the DOM after the data is set.
// 1. The script is loaded after the data is set due to network issues.
// 2. The script is loaded after the data is set due to the script being loaded lately for any reason.

alreadySetData = this.data;
delete this.data;
} catch {
//the error happens in regular cases when this.data is not set.
//in this situation, the #props has not been initialized yet and the getter function is being called which causes the error.
//so, we catch the error and ignore it.
}

if (alreadySetData) {
console.warn("The data has been set before the element is defined in the DOM. This means a potenial of delay rendering. Please make sure to load the widget script before setting the data.");
}

this.#props = new Observable<Props>(alreadySetData);
}

protected get reactComponent(): React.ComponentType<Props> {
throw new Error("You must implement this method in a subclass.");
Expand Down
2 changes: 0 additions & 2 deletions samples/react/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
<link href="favicon.png" rel="icon">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React app</title>
<link rel="modulepreload" href="/cdn/movie-widgets/index.js" />
<script type="module" src="/cdn/movie-widgets/index.js"></script>
<script type="module" src="index.js" blocking="render" async="false"></script>
</head>
<body>
Expand Down
23 changes: 19 additions & 4 deletions samples/react/public/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { MovieWidgets } from "/cdn/movie-widgets/index.js";

MovieWidgets.initialize({
theme: document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light"
});
const head = document.getElementsByTagName("head")[0];

const link = document.createElement("link");
link.rel = "modulepreload";
link.href = "/cdn/movie-widgets/index.js";

head.insertBefore(link, head.firstChild);

const script = document.createElement("script");
script.src = "/cdn/movie-widgets/index.js";
script.type = "module";
script.onload = () => {
window.MovieWidgets.initialize({
theme: document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light"
});
};

head.appendChild(script);

2 changes: 1 addition & 1 deletion samples/react/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, Div, Flex } from "@workleap/orbiter-ui";
import { Link, Outlet } from "react-router-dom";

import { MovieFinder, SelectedMovie } from "../../widgets/movie-widgets/dist/react/index.js";
import { MovieFinder, SelectedMovie } from "@samples/movie-widgets/react";

import { AppContextProvider, useAppContext } from "./AppContext.tsx";

Expand Down
2 changes: 1 addition & 1 deletion samples/react/src/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MovieDetails, MoviePopup, SelectedMovie, Ticket } from "@samples/movie-widgets/react";
import { Flex } from "@workleap/orbiter-ui";
import { useState } from "react";
import { MovieDetails, MoviePopup, SelectedMovie, Ticket } from "../../widgets/movie-widgets/dist/react/index.js";

export function MainPage() {
const [boughtTickets, setBoughtTickets] = useState<{ key: string; count:number; title: string }[]>([]);
Expand Down
2 changes: 1 addition & 1 deletion samples/react/src/StorePage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MovieDetails, SelectedMovie } from "@samples/movie-widgets/react";
import { Checkbox } from "@workleap/orbiter-ui";
import { useState } from "react";
import { MovieDetails, SelectedMovie } from "../../widgets/movie-widgets/dist/react/index.js";

export function StorePage() {
const [showRanking, setShowRanking] = useState(false);
Expand Down

0 comments on commit 1154b06

Please sign in to comment.