Svelte support
Closed this issue · 5 comments
As @adrianhunter pointed out here #43 (comment), svelte support could be cheap.
We already know how to write the query component (following @adrianhunter example):
<script lang="typescript">
import { resolved } from "gqless";
let _$props = $props;
async function resolveData(_) {
await resolved(
() =>
_$props.$slots && _$props.$slots.default[0](_$props.$scope.ctx)
);
}
</script>
{#await resolveData($props) then value}
<slot />
{/await}
and using in our component like this:
<script lang="typescript">
let value = 0;
$: filter = {
limit: 10,
where: { hash: { _ilike: `${value}%` } }
};
$: txs = query.tx(filter);
</script>
<input type="text" bind:value class="input" placeholder="search hash" />
<button
on:click={() => {
txs[0].hash = 'asd';
}}>
change hash
</button>
<Query>
{#each txs as item}
<div>{item.firstSeen} : {item.hash}</div>
{/each}
</Query>
This works well and is almost feature complete, but we miss a way to share cache updates between components.
I have implemented the idea using extensions:
const subscriptions = new Map<string, Function>();
export const User = function(user) {
return {
subscribe(subscription: (v) => void) {
const id = v4();
subscriptions.set(id, subscription);
subscription(user);
return () => subscriptions.delete(id);
},
set(value: any) {
subscriptions.forEach((s) => {
s({ ...(user || {}), ...value });
});
},
};
};
we use this in our components exactly like we would using a store in svelte, by adding a $ in front.
<Query>
<!-- {#if $me.LastName}Hello {$me.LastName}{/if} -->
{:else}
<Redirect to="/login" />
{/if}
</Query>
By using gqless like this we get all the functionality, with one component and one extension, however we need to add the 2 methods for every type which is not ideal.
We may generate those methods with the cli, I dont know how that would work with the proxy system, can we transform every gql type generated in potential store by adding subscribe and set methods? I can try a pull request in that direction.
It's not the best idea to apply an extension to all types. While you can do it, extensions do have overhead, and are mostly meant for user-land only (rather than integration layer func).
I presume svelte is checking for the .subscribe method, and that's why you're using extensions? if so, would be best-suited as another API
yes I need to have the methods set and subscribe on the type to be able to watch for changes. What do you mean by another API? Having smth like extensions but at generation time? smth that is not shown to the end user?
I'd like to try your approach @ticruz38. Do you have a sample project by chance? I'm not quite sure how it all fits together.
The best I could do was this
import { update, getAccessor } from "gqless";
import { Writable } from "svelte";
export function watch<T>(data: T): Writable<T> {
const acc = getAccessor(data);
const d = writable(data, (set) => {
acc.onValueChange((next, prev) => {
if (acc.data == null) {
return;
}
set(acc.data);
});
acc.onDataChange((prev) => {
set(acc.data);
});
});
d.set = (v: any) => {
update(data, v);
acc.onDataChange.emit(null);
};
return d;
}
the watch function would then be used like this:
<script>
const me = watch(query.me);
</script>
Hello {$me.firstName}
This works pretty well, except when the value fetched is null, this breaks the proxy, you want get subsequent changes for this accessor. I'm not sure if the problem is general to this library or if the react wrapper deal with it properly.
Updating the gqless store is as simple as Svelte is => $me.firstName = "Ticruz"
Hi @ticruz38, We have just released the new version v2 of gqless, please visit https://gqless.com for instructions, it now has Mutation support, Subscriptions support, Normalization and much more, feel free to enter our discord server, to open an issue or a discussion about anything related, thanks you for your contribution! 🎉 .
About this specific feature request, it's completely feasible due to the new design, feel free to open a new issue requesting it and discussing it's implementation via the same issue, or in out Discord