1

CLARIFICATION
I realize that I did not write my question very well (sorry about that). I have therefore rewritten my question and the accompanying examples.


I want to create a number of components using the Render Function in VueJS. Each function will basically be the same format, just the data inserted will change. It seems to me rather repetitive (and not at all DRY) to rewrite the render function each and every time just to create different components (when the basic structure of each component is the same).

As such, what I'd like to do is create an array that holds all the data I want to use for the various different components. I then want to loop through that data, running the Render Function each time.

For instance, imagine that this is my data:

var components = [
        { name: 'label',
          props: ['tag', 'type', 'size', 'color', 'direction'],
          class: 'label',
          tagOption: true,
          tag: 'div'},

        { name: 'icon',
          props: ['type', 'size', 'color'],
          class: 'icon',
          tagOption: false,
          tag: 'i'}
    ]

Running the loop on this data would be the equivalent of writing the Render Function twice as follows:

Label Component

export label {
    props: ['tag', 'type', 'size', 'color', 'direction'],
    render(createElement) {
        let classes = ['ui', 'label']
        if (this.type) { classes.push(this.type) }
        if (this.size) { classes.push(this.size) }
        if (this.color) { classes.push(this.color) }
        if (this.direction) { classes.push(this.direction) }

        return createElement(
            this.tag || 'div',
            { class: classes },
            this.$slots.default
        );
    }
}

Icon Component

export icon {
    props: ['type', 'size', 'color'],
    render(createElement) {
        let classes = ['ui', 'label']
        if (this.type) { classes.push(this.type) }
        if (this.size) { classes.push(this.size) }
        if (this.color) { classes.push(this.color) }

        return createElement(
            'i',
            { class: classes },
            this.$slots.default
        );
    }
}

In summary

This is what I do not want to do: Have a label.js (or label.vue) file which runs the Render Function to create a label component and a separate icon.js file which runs the basically the exact same Render Function to create an icon component

This is what I do want to do: Have one file which would loop through an array of data, running and exporting that data each loop through the Render Function.

Is this possible? If so, any ideas on how to do it?

Thanks.

3

2 Answers 2

1

There are a couple ways you could do this. Here is one registering components as global components.

for (let tmpl of components){
  let render = function(createElement){
    let classes = ["ui", tmpl.name]  
    const props = tmpl.props.slice(1)
    for (let prop of props)
      if (this[prop]) classes.push(this[prop])

    return createElement(tmpl.tag, {class: classes}, this.$slots.default)
  }

  Vue.component(`${tmpl.name}-component`,{
    props: tmpl.props,
    render
  })
}

new Vue({
  el:"#app"
})

Example template:

<div id="app">
  <label-component direction="horizontal">stuff</label-component>
  <icon-component size="large" color="blue">Other stuff</icon-component>
</div>

Example.

If you didn't want global components you could do this.

let components = {}
for (let tmpl of componentTemplates){
  let render = function(createElement){
    let classes = ["ui", tmpl.name]  
    const props = tmpl.props.slice(1)
    for (let prop of props)
      if (this[prop]) classes.push(this[prop])

    return createElement(tmpl.tag, {class: classes}, this.$slots.default)
  }

  components[`${tmpl.name}-component`] = {
    props: tmpl.props,
    render 
  }
}

new Vue({
  el:"#app",
  components
})

Example.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for you answer. I was not clear in what I was asking (sorry about that). I rewrote my question to better explain my question.
Thanks - that is very helpful (again)
1

You can generate your components outside of Vue declaration:

(I renamed components because Vue doesn't like it when you use common HTML tag names like label or icon as components names)

const components = [
  { name: 'child1',
    props: ['tag', 'type', 'size', 'color', 'direction'],
    class: 'label',
    tagOption: true,
    tag: 'div'},
  { name: 'child2',
    props: ['type', 'size', 'color'],
    class: 'icon',
    tagOption: false,
    tag: 'i'}
];

// function to generate components
const generateComponents = components => components
  .map(c => Object.assign({[c.name] : {
    props: c.props,
    render: function(createElement) {
        const classes = ['ui', 'label'];
        classes.push(c.class); // I suppose you've forgot this?
        if (this.type) { classes.push(this.type) };
        if (this.size) { classes.push(this.size) };
        if (this.color) { classes.push(this.color) };
        if (this.direction) { classes.push(this.direction) };

        return createElement(
            c.tag || 'div',
            { class: classes },
            this.$slots.default
        );
      }
    }
  })).reduce((a, v) => Object.assign(a, v), {});

// here you are
const myComponents = generateComponents(components);

new Vue({
  el: '#app',
  components: myComponents
});
.green { color: green; font-weight: bold }
.mytype { background-color: beige }
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <child1 type="mytype">hello</child1>
  <child2 color="green">hello again</child2>
</div>

1 Comment

Thanks for your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.