vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.46k stars 33.65k forks source link

Override compiled template using x-template usage #4795

Closed dimitri-koenig closed 7 years ago

dimitri-koenig commented 7 years ago

Hi

Using components I can provide the template inside the component together with the script code. But as soon as I provide such a template I cannot use the x-template method to override the template. Even writing an own render function which checks for an existing x-template and uses that if found, or else the template inside the component, does not work.

Would be super helpful if there would be a way to override templates explicitly using x-template feature provided within the html file.

yyx990803 commented 7 years ago

Although I don't quite understand the use case, you can do something like this:

components: {
  child: {
    template: document.querySelector('#child-template') ? '#child-template' : '<div>...</div>'
  }
}
dimitri-koenig commented 7 years ago

Hm, does not quite solve my issue :-/

My use case is this: I want to compile a whole application with templates within one single component file (with style, script and template sections) and deliver it within a package. But for easier customization where only template modifications are needed, you shouldn't have to go directly into the components but rather create a "x-template" within your html file which overrides the already provided template in the one-component-file.

Any idea on how I could solve that?

yyx990803 commented 7 years ago

Are you using single file components?

dimitri-koenig commented 7 years ago

The application is using single file components, yes.

yyx990803 commented 7 years ago

In that case this is not possible because single-file components' templates are pre-compiled at build time. Also allowing users to arbitrarily overwrite component template sounds messy to me. Maybe you should consider using slots to allow content composition instead.

dimitri-koenig commented 7 years ago

Just in case someone ever wants a work-around/solution to this, I solved it this way:

Before starting your application you should override Vue.prototype.$mount:

import Vue from 'vue';

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el, hydrating) {
  const options = this.$options;

  if (options.templateOverride && typeof options.templateOverride === 'string' && options.templateOverride.charAt(0) === '#' && document.querySelector(options.templateOverride)) {
    let renderFunctions = Vue.compile(document.querySelector(options.templateOverride).innerHTML);
    Object.assign(options, renderFunctions);
  }

  return mount.call(this, el, hydrating);
}

In your vue component now you can say which override x-template should be used if there is any. Example:

<script>
export default {
  name: 'hello',
  templateOverride: '#hello-override',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

So if Vue finds some x-template in the dom with that override id it will take it. Example:

    <div id="app"></div>

    <script type="x-template" id="hello-override">
      <div id="hello">
        <h1>Override: {{ msg }}</h1>
      </div>
    </script>

    <script src="dist/build.js"></script>

That way I can write components in the one component file vue way but also give it away for others to override just the template if needed.

dimitri-koenig commented 7 years ago

@yyx990803 If I create a pull request for this to make it into the core how are the chances to really make into the core?

Again, the background is this: Working within an agency you have to write applications which can be just included within a website with most of the only some minimal template modifications. Without compiling it again someone else should be able to change the template as the customer needs it.

posva commented 7 years ago

@dimitri-koenig Thanks for sharing your solution! Since the solution is simple enough and your use case is very specific there's no need to add it to core

ela4490 commented 6 years ago

@yyx990803, @dimitri-koenig : I also have a similar requirement in which I want to do easy customization where only template modifications are needed. How can i override the templates?

I tried the solution given by @dimitri-koenig but somehomw its not working for me.

@dimitri-koenig : One quick question from the solution. In which file do I need to add the override id. I added the below contents in index.html but it did not work for me. Can you please explain a bit more? I'm relatively new to VueJS.

So if Vue finds some x-template in the dom with that override id it will take it. Example:

    <div id="app"></div>

    <script type="x-template" id="hello-override">
      <div id="hello">
        <h1>Override: {{ msg }}</h1>
      </div>
    </script>

    <script src="dist/build.js"></script>
gtempesta commented 3 years ago

@dimitri-koenig I have tried your solution and it worked, but I've discovered that using extends is better for my use case.

I am also using a different template for marketing reasons, while the logic stays the same, so I have removed the template from the original component and created two new components that extend the first one, each one with only a template tag (besides the name and theextends keyword which reside in the script tag): this allows for maximum flexibility without changing the core of the library.

Hope my solution may help other people with a similar use case.

gtempesta commented 3 years ago

Here is an example:

Base Component

<script>
export default {
    name: 'ProductConfiguration',
    computed: {
        // computed...
    },
    methods: {
        // methods...
    },
    // ...other properties
};
</script>
<style lang="scss" scoped>
    /* you can include styles here or in the extended component */
</style>

Extended Component

<template>
    <div>
        <!-- here you define a template -->
    </div>
</template>
<script>
import ProductConfiguration from '../ProductConfiguration.vue';

export default {
    name: 'ProductLayout',
    extends: ProductConfiguration,
    // nothing else is needed here
};
</script>
<style lang="scss" scoped>
    /* you can include styles here or in the base component */
</style>

If you need a different template (or as many as you want) you just need to create another component that extends the Base Component.

dimitri-koenig commented 3 years ago

Hi @gtempesta: I'm not sure wether we are taking about the same. In my case I want to load via Githubissues.

  • Githubissues is a development platform for aggregating issues.