Vue: "class" and "style" attributes have no effect on functional SFC components

Created on 9 Apr 2018  路  8Comments  路  Source: vuejs/vue

Version

2.5.16

Reproduction link

https://github.com/padcom/vue-functional-no-class-passed

Steps to reproduce

What is expected?

Functional component is painted in green/red by applying the items CSS class and color: red style

What is actually happening?

The items CSS class nor style is not applied


I am mostly using the added class in components for BEM-style annotations where a component is at the same time an element from the point of view of the container and a block from its personal point of view thus allowing me to delegate certain properties to the container, like positioning for example.
I'd like to use that same principle in functional components, for example with transition-group that has the option to specify the tag attribute. It works with statefull components but functional components fail to pass the class and style properties when rendering.

Please note that the functional components declared in the example repository do contain the v-bind="data.attrs" and v-on="listeners" attributes on the root node as described in the documentation.

Most helpful comment

This would also work in an SFC but it is ugly:

<template functional>
  <div v-bind="data.attrs" :class="data.staticClass" :style="data.staticStyle">
    Functional component
  </div>
</template>

All 8 comments

I think the following example workaround would work:

MyUlFunc.js
import './MyUlFunc.css'

export default {
  functional: true,
  render(h, context) {
    const data = {
      ...context.data,
      props: {
       ...context.props,
        tag: 'ul',
        name: 'slide'
      }
    }
    return h('transition-group', data, context.children)
  }
}

MyUlFunc.css

.slide-move {
  transition: transform 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

This will render everything that I need regardless of #7557. With that I think a workaround exists but it still would be great to be able to do that in an SFC.

This would also work in an SFC but it is ugly:

<template functional>
  <div v-bind="data.attrs" :class="data.staticClass" :style="data.staticStyle">
    Functional component
  </div>
</template>

Hi, thanks for filling this issue.

Functional components don't share the behavior of automatically applying attributes/classes/styles as stateful components, which has been documented here:

On normal components, attributes not defined as props are automatically added to the root element of the component, replacing or intelligently merging with any existing attributes of the same name.

Functional components, however, require you to explicitly define this behavior

Skipping the auto binding behavior would remove the performance overhead of always merging classes/styles/attirbutes on normal components. This is how functional components are made to be fast, by design. What you're using in the last comment is actually the correct way, and not a workaround.

... but it's ugly :(

Btw - there is nothing about passing on class and style to the functional component and what's even more annoying it does work with regular functional component, written in JavaScript as depicted in the previous example. Can't you at least make that the functional components behave the same without additional plumbing?

now that we have inheritAttrs, it may be possible in the future to change this behaviour, it's all about flexibility. In the past, templates for functional components wasn't possible, so it made sense to have to pass them in js (and also easier):

render: (h, { data }) => h('div', data, [...])
// jsx
render: (h, { data }) => <div {...data}>...</div>

I was thinking: would it be possible to use a .vue file with a render function such as the one you mentioned? What I am looking for is a single file containing everything (like the SFC do) because I fail to see a point of having a separate .css file just to describe some animation keyframes.

I think I have it! SFC's seem to be very powerful and that I like very much:

<script>
export default {
  functional: true,
  render: (h, { data, children }) => (
    <transition-group { ...data} tag="ul" name="slide">
      { children }
    </transition-group>
  )
}
</script>

<style>
.slide-move {
  transition: transform 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
</style>

That is very elegant :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ferry77 picture ferry77  路  67Comments

yyx990803 picture yyx990803  路  210Comments

Akryum picture Akryum  路  34Comments

yyx990803 picture yyx990803  路  48Comments

chrisvfritz picture chrisvfritz  路  46Comments