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

Functional single file component with components option. #7492

Open
terrierscript opened this issue Jan 21, 2018 · 13 comments

Comments

Projects
None yet
@terrierscript
Copy link

commented Jan 21, 2018

Version

2.5.13

Reproduction link

NG pattern (functional)
https://codesandbox.io/s/004vv2onw0

OK pattern (no functional)
https://codesandbox.io/s/q9k5q8qq56

Steps to reproduce

I found can't use components option when functional single file component.

<template functional>
  <div>
    <some-children />
  </div>
</template>

<script>
import SomeChildren from "./SomeChildren"

export default {
  components: {
    SomeChildren
  }
}
</script>

It's occure Unknown custom element.

What is expected?

Not occure Unknown custom element and use child component

What is actually happening?

It's occure Unknown custom element


In workaround, it not occure when use Vue.component.

import Vue from "vue"
import SomeChildren from "./SomeChildren"
Vue.component("some-children", SomeChildren);

export default {}

// can use  <some-children />
@jingle2008

This comment has been minimized.

Copy link

commented Feb 10, 2018

Ran into exact same issue here, nice workaround!!!

@gregolai

This comment has been minimized.

Copy link

commented Feb 10, 2018

Just learning Vue and struggling with this for the past few hours! Thanks for bringing it up!

@ywwhack

This comment has been minimized.

Copy link

commented Mar 1, 2018

Here is another workaround, it avoids global component, but looks not pretty

<template functional>
  <div>
    <component :is="props.components.SomeChildren"></component>
  </div>
</template>

<script>
import SomeChildren from "./SomeChildren.vue";
export default {
  props: {
    components: {
      type: Object,
      default() {
        return {
          SomeChildren
        };
      }
    }
  }
};
</script>
@alexsasharegan

This comment has been minimized.

Copy link

commented Mar 1, 2018

I think it's worth mentioning here that the error message is quite unintuitive. The Unkown custom element error bubbles up to the first instance component. If for some reason the feature for local functional component registration does not get implemented, at least add a dev warning that says something to the effect of Invalid property "components" on functional component X.

Also, as awkward as it is, registering the unknown component in the first parent instance component clears the error without polluting the global component name scope. It's a strange coupling of components though. Choose your hack for now I suppose.

@caikan

This comment has been minimized.

Copy link

commented Apr 8, 2018

@ywwhack Your workaround is great! I made some improvements. We can use injections instead of props, so that props will not be polluted, and the code looks a little prettier.

<template functional>
  <div>
    <component :is="injections.components.SomeChildren"></component>
  </div>
</template>

<script>
import SomeChildren from "./SomeChildren.vue";
export default {
  inject: {
    components: {
      default: {
        SomeChildren
      }
    }
  }
};
</script>
@darkylmnx

This comment has been minimized.

Copy link

commented Apr 30, 2018

any update on implementing components option in functional components ?

The workarounds are good but that seems pretty hacky IMO especially when mentioning that <component :is="injections.components.SomeChildren"></component> must be fore dynamic components and not for known components

privatenumber pushed a commit to privatenumber/vue that referenced this issue May 7, 2018

Hiroki Osame
feat(functional): Support components hash in functional components
Adds support for the components hash to be used in functional components

fix vuejs#7492

@privatenumber privatenumber referenced a pull request that will close this issue May 7, 2018

Open

Support `components` obj in functional components (fix #7492) (fix #6872) #8143

7 of 13 tasks complete

@sodatea sodatea added the has PR label Jun 10, 2018

@KumaCool

This comment has been minimized.

Copy link

commented Oct 23, 2018

parent:

// template functional 
    v-list
        component(
            :is='injections.components.myListItem'
            v-for='item in props.data',
            :key='item.title',
            :data='item')

// script
import myListItem from './listItem'
export default {
    name: 'myList',
    inject: {
        components: {
            default: {myListItem}
        }
    }
}

children:

// template functional
    v-list-group(v-if='Array.isArray(props.data.children)')
        v-list-tile(slot='activator')
            v-list-tile-content
                v-list-tile-title {{props.data.title}}
        my-list-item(
            v-for='item in props.data.children',
            :key='item.title',
            :data='item')
    v-list-tile(v-else)
        v-list-tile-content
            v-list-tile-title {{props.data.title}}

// script
export default {
    name: 'myListItem'
}

app:

// template
    #app
        my-list(:data='list')

// script
import myList from './list'
export default {
    name: 'App',
    components: {myList},
    data() {
        return {
            list: [
                {title: 1},
                {title: 2},
                {title: 3},
                {
                    title: 4,
                    children: [
                        {title: 41},
                        {title: 42},
                        {title: 43}
                    ]
                },
                {title: 5}
            ]
        }
    }
}

children error: Unknown custom element
multi-level functional components nesting failed.
help me!~

@Alendorff

This comment has been minimized.

Copy link

commented Dec 24, 2018

Any ideas how to implement dynamic async components inside functional components?

E.g. functional should be simple wrapper like

<component :is="componentName" /> 

where componentName is one of the dynamically imported components?

For non-functional component it looks like this:

<template>
  <component :is="componentName" v-bind="$attrs"/>
</template>

<script>
const someCondition = Math.random() > 0.5

export default {
  name: 'PolicyRequestInfo',
  components: {
    FirstDynamic: () => import('./FirstDynamic'),
    SecondDynamic: () => import('./SecondDynamic')
  },
  computed: {
    componentName() {
      return SomeCondition ? 'FirstDynamic' : 'SecondDynamic'
    }
  }
}
</script>

For functional component I have no idea how to make it work.

@meteorlxy

This comment has been minimized.

Copy link

commented Jan 7, 2019

vue/types/options.d.ts

Lines 123 to 133 in 7075408

export interface FunctionalComponentOptions<Props = DefaultProps, PropDefs = PropsDefinition<Props>> {
name?: string;
props?: PropDefs;
model?: {
prop?: string;
event?: string;
};
inject?: InjectOptions;
functional: boolean;
render?(this: undefined, createElement: CreateElement, context: RenderContext<Props>): VNode | VNode[];
}

The type declaration shows that, functional component of current version can't accept components option in fact.

Of course a possible solution is to use the render function:

<script>
import SomeChildren from './SomeChildren.vue'

export default {
  render (h) {
	return h('div', [
      h(SomeChildren),
    ])
  }
}
</script>
@Alendorff

This comment has been minimized.

Copy link

commented Jan 8, 2019

Yep, it actually works with render functions. I use something like that now:

<script>
const component = Math.random() > 0.5 
    ? () => import('./compA') 
    : () => import('./compB')

export default {
  functional: true,
  render(h, context) {
    return h(component, context.data, context.children)
  }
}
</script>
@ycmjason

This comment has been minimized.

Copy link

commented Feb 12, 2019

Would really love to see components option being supported for functional components.

@dcwarwick

This comment has been minimized.

Copy link

commented Feb 13, 2019

This feature would certainly extend the usefulness of functional components in a natural way. I for one had not realised that components wasn't supported in functional components until we tried it and found out. We have a library of components as SFCs, and wanted to mark some of the simple ones with no internal state as functional.

At the moment, if a component is a SFC and has child component dependencies and we want to mark it functional, the options would seem to be:

  • convert the template to a render function
  • globally register all the child components required
  • an ingenious workaround described above by @ywwhack @caikan and others that loads the dependency child components using props or inject and then references them with a <component :is="...">

However, in a library of many interlinked components all done as SFCs it is desirable to retain the template for consistency with the other components, and undesirable to globally register names. The workaround for all its ingenuity looks rather messy/fiddly and would require explanation for maintenance. It would be much neater just to be able to declare child components with components exactly as for non-functional SFCs.

Fix #8143 looks good and ready to go. If there isn't a reason not to, can it be delivered? We've held off from making any of our components functional atm, and would use it right away :-)

@decademoon

This comment has been minimized.

Copy link
Contributor

commented Feb 13, 2019

I for one had not realised that components wasn't supported in functional components until we tried it and found out.

That was my experience too. I remember being quite surprised when I discovered components weren't supported in functional components. This really, really diminished the usefulness of functional components and I rarely use them because of this.

I do use JSX in my Vue projects because sometimes I need to drop down into the render function, so this isn't technically a huge issue for me, but I like to avoid JSX whenever possible.

I'm hoping Vue 3 will improve on this in some way. React already does functional components well.

an ingenious workaround described above by @ywwhack @caikan and others that loads the dependency child components using props or inject and then references them with a <component :is="...">

I'm not a fan of this hack TBH. I will just stay clear of functional components until this issue is officially resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.