Passing non-prop attributes to component sub-elements

I’m building a small UI component library, the template for each will be something like the following:

<div class="ui field">
    <label>
    <div>
        <!-- component internals -->
    </div>
</div>

I know from the docs that non-prop attributes (i.e. class, style, etc) will be appended into the root element:

Resulting in something like this:

<div class="ui field foo bar" style="foo: bar">
    ...
</div>

Is there a way to pass non-prop attributes to any child elements, for example, passing some additional constraints to a child <input> element, for example?

<div class="ui field">
    <label>
    <div>
        <input foo="1" bar="2" baz="3">
    </div>
</div>

The only thing I can think is to have a prop called attributes into which a string of attributes can be passed, and these are set in mounted().

Not very “Vue” but it should work.

Yes, it’s a brand new feature! :smiley:
https://github.com/vuejs/vue/releases/tag/v2.4.0

Ah, great!

How are child attributes defined on the component markup?

I was having my own think about precisely this, and thought something like this would be nice:

Template:

<template>
   <div>
      <label ref="label">
      <input ref="input">
   </div>
<template>

Component declaration:

<component
   class="foo"
   label.class="bar"
   input.class="baz"
></component>

Output:

<div class="foo">
   <label class="bar">
   <input class="baz">
</div>
<component
   class="foo"
   input.class="bar"
>
</component>

It took me a minute to understand that.

You would have to do that yourself. All Vue gives you is this.$attrs, which is an object with all the attributes that were not matched to props.

{
  'input.class': 'bar'
}

But it should be quite trivial to write a function that reads the keys and splits by ., using the first section as a ref.

1 Like

That’s actually pretty sweet that it’s that flexible.

But it should be quite trivial to write a function that reads the keys and splits by ., using the first section as a ref.

And, yes :slight_smile: Here it is:

function getAttrs(attrs, name, delim = '.') {
    const prefix = name + delim
    return Object
        .keys(attrs)
        .filter(key => key.indexOf(prefix) === 0)
        .reduce(function (output, key) {
            output[key.replace(prefix, '')] = attrs[key]
            return output
        }, {})
}

var $attrs = {
    'input.foo': 1,
    'input.bar': 2,
    'label.baz': 3,
}

getAttrs($attrs, 'input')

How does one attach the attributes? Using the spread operator as you outlined in your comment?

<dropdown v-bind="{ ...$props, $attributes.type }"/>

Where? To the component (in the parent) or to the input in your component, as per your example?

To elements within the component, in my example, the input

I your example, like this:

<input v-bind="getAttrs($attrs)"

no need to use the rest spread unless you also want to add other dnymaic attributes inline

Ok, so just simply v-bind.

Now the release note example is clear, along with the listeners:

<div>
  <input v-bind="$attrs" v-on="$listeners">
</div>

Thanks! This is great :slight_smile:

Look like it might be time to review the core docs.

v-bind has all this info in it: https://vuejs.org/v2/api/#v-bind

v-bind="$attrs" v-on="$listeners" solves the problem for most things passed into the component, but what about forwarding refs (e.g https://reactjs.org/docs/forwarding-refs.html)? In my case I’ve wrapped a 3rd party component and want to pass the attrs, listeners AND ref (AKA everything) through to the wrapped component.