Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data that flows up: out directive properties #4271

Closed
c8y3 opened this issue Jan 16, 2020 · 6 comments
Closed

Data that flows up: out directive properties #4271

c8y3 opened this issue Jan 16, 2020 · 6 comments

Comments

@c8y3
Copy link

c8y3 commented Jan 16, 2020

Is your feature request related to a problem? Please describe.
Data for properties flows down. Directive bind allows data to flow both ways.
But there are times when I need to propagate data upward.
For instance, this is the case whenever I have to aggregate user data from different widgets and send them to the server via a request. In this case, I usually define an intermediate component that both defines the layouts of the widgets and combine their data.

Describe the solution you'd like
It would be nice to have an out directive:
<Component out:result={myVariable} />
So that whenever the value of property result changes within the Component, then variable myVariable would be updated.

In the case of the aggregation of two components it would look like this:

[...]
    let value1;
    let value2;
    let output;

    $: output = combine(value1, value2);
</script>

<Component1 out:value={value1} />
<Component2 out:value={value2} />
Result: {output}

Describe alternatives you've considered
A workaround that works consists in having properties that are callbacks. The previous example can be written like this:

[...]
    let value1;
    let value2;
    let output;

    $: output = combine(value1, value2);
</script>

<Component1 outValue={v => value1 = v} />
<Component2 outValue={v => value2 = v} />
Result: {output}

How important is this feature to you?
I believe it is important to be able to easily follow the flow of data throughout the application. Having to use functional properties in order to invert the usual flow of property data so that it flows up, makes the code less readable and more verbose.

Additional context

@Conduitry
Copy link
Member

This can be achieved today by two-way binding in the parent to an exported store in the child. If the child does export const, then the parent can't change the child's version of the read-only prop. And if the store is also read-only, the parent can subscribe to changes to the value, but can't set values itself.

@Conduitry
Copy link
Member

App.svelte

<script>
	let value = { subscribe: () => () => {} };
	import Child from './Child.svelte';
</script>

<Child bind:value/>

{$value}

Child.svelte

<script>
	import { writable } from 'svelte/store';
	import { onMount } from 'svelte';
	const store = writable(0);
	export const value = { subscribe: store.subscribe };
	onMount(() => {
		const interval = setInterval(() => store.set(Math.random()), 1000);
		return () => clearInterval(interval);
	});
</script>

The only unfortunate thing here is that the store in the parent does need to be given an initial discarded value, so that you can subscribe to it. If you're fine with creating the actual store in the parent, then you can just pass that into the child, no two-way binding necessary, but at that point you lose the data-goes-up-only feature.

@Conduitry
Copy link
Member

See also #2181 for thoughts on removing the need for value to be a valid store from the very beginning.

@vipero07
Copy link

vipero07 commented Jan 22, 2020

Here is @Conduitry 's counter example where you make the store in the parent, but as he pointed out it allows two way data flow.

https://svelte.dev/repl/962f0ded41074752bd3cdcd6ae90cab1?version=3.17.2

If you have the state of the application (or your aggregate in this case) in a store, the callbacks actually makes the most sense though, since you'd use those to call .update on the store like
https://svelte.dev/repl/c3164f9cc72d43cfb9bbb535a41c814a?version=3.17.2

@Conduitry
Copy link
Member

As of 3.18, attempting to subscribe to a nullish store is a no-op, so the { subscribe: () => () => {} } workaround in App.svelte in my above example is now unnecessary. This allows you to have up-only data flow.

I don't think this situation is common enough to warrant its own syntax, when passing up a store and passing down callbacks both work.

@c8y3
Copy link
Author

c8y3 commented Feb 1, 2020

Thank you for the various answers.
After all, I think I too prefer the callback syntax. It seems to me the bind syntax should be kept for leaf widgets only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants