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

Split attrs and props #3015

Closed
andrei-kochetygov opened this issue Jun 12, 2019 · 9 comments
Closed

Split attrs and props #3015

andrei-kochetygov opened this issue Jun 12, 2019 · 9 comments

Comments

@andrei-kochetygov
Copy link

andrei-kochetygov commented Jun 12, 2019

Quick search through issues haven't results, so I will create this.

Is it technically possible to split native attributes and properties into $$attrs and $$props for cases where we need to bind only native attributes to some element?

Something like in Vue realization:

<template>
    <label>
        {{ label }}
        <input 
            v-bind="$attrs" 
         >
    </label>
</template>

<script>
    export default {
        inheritAttrs: false,
    }
</script>

In Vue it's much cleaner, because component have only one root element. So it's logical to bind attributes to first element. On the other hand, in Svelte it's possible to have several root elements and this is great ability, but may be it's a good idea to allow developers manually control native attributes bindings?

Some stupid example:

// Action.svelte
<button {...$$attrs}>Trigger</button>

<script>
    export let endpoint, method;
</script>

// App.svelte
<Action id="some_id" endpoint="http://api.site.com/posts/1/" method="delete"></Action>

<script>
    import Action from './Action.svelte';
</script>

// Output
<button id="some_id">Trigger</button>

I think this may be useful in cases when we have many properties and don't want to see all properties zoo in markup. Or it may contain something like unregistered properties, which will exclude all explicitly exported props.

However, if we back to the tutorial. It can be bad idea, because of:

Conversely, if you need to reference all the props that were passed into a component, including ones that weren't declared with export, you can do so by accessing $$props directly. It's not generally recommended, as it's difficult for Svelte to optimise, but it's useful in rare cases.

What do you think?

@meiseayoung
Copy link

i think is very important to support this fature;
in many case we want styled an component, we can do that set style property at component in Vue like <Component :style="style">,Vue will mixed the style property in component root element; in Svelte we have to export style property or set style property at component root element as $$props.style manual

@Windvis
Copy link

Windvis commented Aug 17, 2019

I would love to see something like this in Svelte!

Ember has something similar when you use the AngleBrackets Invocation syntax. Components are invoked like this:

<SomeComponent @argumentName class="class-from-parent" />

Inside the component template you can "spread" the html attributes like this (splattributes):

<div class="component-class" ...attributes>content</div>

To have the following rendered html

<div class="component-class class-from-parent">content</div>

class is just an example, everything passed in without an @ prefix will be considered an html attribute.

The main use case for this is passing in classes to position the component inside other components. Or passing in data-test attributes for testing purposes.

In Svelte you would have to manually code this for each component, which isn't ideal.

@arggh
Copy link
Contributor

arggh commented Feb 16, 2020

Yesterday, while building an <Input/> component, I was hoping I could easily access any props passed to component that were not being explicitly used by the component, and use them as attributes for the native input element, like so:

<!-- TextInput.svelte -->
<script>
export let value;
export let large = false;
</script>

<input type="text" class:large bind:value {...$attrs} />
<!-- $attrs becomes { maxlength: "128" } as defined in App.svelte -->
<!-- App.svelte -->
<script>
import TextInput from './TextInput.svelte';

let name = '';
</script>

<TextInput bind:value={name} maxlength="128" large/>

@neurocmd
Copy link

Yesterday, while building an <Input/> component, I was hoping I could easily access any props passed to component that were not being explicitly used by the component, and use them as attributes for the native input element, like so:

<!-- TextInput.svelte -->
<script>
export let value;
export let large = false;
</script>

<input type="text" class:large bind:value {...$attrs} />
<!-- $attrs becomes { maxlength: "128" } as defined in App.svelte -->
<!-- App.svelte -->
<script>
import TextInput from './TextInput.svelte';

let name = '';
</script>

<TextInput bind:value={name} maxlength="128" large/>

And how did you solve this problem?

@arggh
Copy link
Contributor

arggh commented Feb 28, 2020

And how did you solve this problem?

Using a reactive declaration like this:

<script>
export let large = false;
export let value;

$: props = omit($$props, 'value', 'large');
</script>

<input type="text" class:large bind:value {...props} />

However, with that approach, you will bump into this bug .

@Conduitry
Copy link
Member

More or less a duplicate of #2930. I think if we were to have something like this, it would probably take the form of a magic global $$rest (name tbd) that includes all props that there aren't explicit exports for.

@neurocmd
Copy link

neurocmd commented Mar 9, 2020

Using a reactive declaration like this:

<script>
export let large = false;
export let value;

$: props = omit($$props, 'value', 'large');
</script>

<input type="text" class:large bind:value {...props} />

This approach makes us duplicate prop names. It might be better to use the internal api:

import { current_component } from 'svelte/internal';

export function omit(obj) {
  const keysToOmit = Object.keys(current_component.$$.props);
  return Object.keys(obj).reduce((acc, key) => {
    if (keysToOmit.indexOf(key) === -1) acc[key] = obj[key]
    return acc
  }, {})
}

and then inside svelte component

$: attrs = omit($$props);

@PatrickG
Copy link
Member

PatrickG commented Mar 9, 2020

Using a reactive declaration like this:

<script>
export let large = false;
export let value;

$: props = omit($$props, 'value', 'large');
</script>

<input type="text" class:large bind:value {...props} />

This approach makes us duplicate prop names. It might be better to use the internal api:

import { current_component } from 'svelte/internal';

export function omit(obj) {
  const keysToOmit = Object.keys(current_component.$$.props);
  return Object.keys(obj).reduce((acc, key) => {
    if (keysToOmit.indexOf(key) === -1) acc[key] = obj[key]
    return acc
  }, {})
}

and then inside svelte component

$: attrs = omit($$props);

There is no need to find new ways of doing this. $$rest is on its way

@neurocmd
Copy link

neurocmd commented Mar 9, 2020

There is no need to find new ways of doing this. $$rest is on its way

Oh thats cool!

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

7 participants