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
208k stars 33.69k forks source link

Directive to skip binding/parsing certain parts #5360

Closed viettranme closed 7 years ago

viettranme commented 7 years ago

What problem does this feature solve?

My sample app structure look like

<div id="app">
    <div class="upper">
        <span v-text="message"/>
    </div>

    <div class="middle">
        <div class="ajax-module" data-src="<ajax-url>">Loading...</div>
    </div>

    <div class="lower">
        <span v-text="message"/>
    </div>
</div>

<script type="text/javascript">
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })
</script>

The issue is that Vue throws an error as the asyn content loaded from 'ajax-url' contains 'script' tag. Is there any way to let Vue skip parsing/binding the '.middle' part?

What does the proposed API look like?

Support a special directive like 'v-skip'

<div class="middle" v-skip></div>
yyx990803 commented 7 years ago

Your template needs to be syntax valid HTML, so escape chars like < or > in attribute values.

viettranme commented 7 years ago
<ajax-url> is just a placeholder for this example. It will be an actual URL.

The problem is that content loaded from this URL contains javascript code, and it causes Vue error.

yyx990803 commented 7 years ago

Then you need to provide a repro. You didn't explain what the code is doing clearly.

viettranme commented 7 years ago

Hello Evan,

My sample index.html file as following:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.min.js"></script>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>

</head>

<body>

<div id="app">
    Hello <span v-text="item.NAME"></span>

    <async-example></async-example>
</div>

<script type="text/javascript">
    $(function() {
        Vue.component('async-example', function (resolve, reject) {
            $.ajax({
                url: 'async.html',
                method: 'GET',
                dataType: 'html'
            }).done(function(data) {
                resolve({
                  template: data   
                })

            });        
        })

        new Vue({
            el: '#app',

            data: {
                item: {NAME: 'VueJS'},
            },
        })                
    });
</script>

</body>

</html>

The file async.html:

<div>
    <h1>Hello</h1>
    <script type='text/javascript'>
        console.log('Hello')
    </script>
</div>

I got the following error on Chrome's console:

[Vue warn]: Error compiling template:

<div>
    <h1>Hello</h1>
    <script type='text/javascript'>
        console.log('Hello')
    </script>
</div>

- Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed.

(found in <AsyncExample>)

Regards, Viet

yyx990803 commented 7 years ago

The warning is pretty clear - you should not embed scripts in templates because of how templates work in Vue. You can however programmatically create <script> tags in the component's created hook.

viettranme commented 7 years ago

Suppose that we in a situation that: (1) we don't have control over the remote contents; (2) the remote contents may contain script tags; (3) we just want to load remote contents and show them as widgets inside the '#app' div element.

Can we achieve the above purpose with Vue?

yyx990803 commented 7 years ago

@viettranme if the remote content is not Vue template (i.e. no bindings, no directives), then you can just use innerHTML. Although it needs to be trusted content to avoid XSS.

viettranme commented 7 years ago

I have moved the remote content loading part to the mounted() callback and things work now.

Thanks Evan.

zero-master commented 6 years ago

I am migrating some legacy apps to Vuejs and I came across a form like this:

<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key="{{ key }}"
            data-description="A Flask Charge"
            data-amount="500"
            data-locale="auto"></script>
  </form>

It's an example from this guide: https://stripe.com/docs/checkout/flask

Vue breaks this form, yes with nice a nice warning which I am grateful for :) but it would nice if there was a way to ask Vue to skip over some tags for us.

However, I'll try the workaround suggested above.

oknasir commented 6 years ago

@zero-master @viettranme

You can add script tags using jquery or document.createElement

Here is a workaround which I use.

let stripScriptTag = $('<script>');

stripScriptTag.attr('class', 'stripe-button');
stripScriptTag.attr('src', 'https://checkout.stripe.com/checkout.js');
stripScriptTag.attr('data-key', 'pk_test_xxxxxxxxxxxxxxx');
stripScriptTag.attr('data-amount', '1999');
stripScriptTag.attr('data-name', 'Stripe Demo');
stripScriptTag.attr('data-description', 'Online course about integrating Stripe');
stripScriptTag.attr('data-image', 'https://stripe.com/img/documentation/checkout/marketplace.png');
stripScriptTag.attr('data-locale', 'auto');
stripScriptTag.attr('data-currency', 'usd');

$('#formStrip').html(stripScriptTag);
jes490 commented 6 years ago

@zero-master @viettranme

Another option is to detach element with script before vue initialization and then reattach it after vue init. It will be helpful if you have two or more inline scripts which depends on each other.


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.min.js"></script>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>

</head>

<body>

<div id="app">
    Hello <span v-text="item.NAME"></span>
   <div id="problem-script">
       <script type="text/javascript" src="problem.js"></script>
   </div>
</div>

<script type="text/javascript">
        let script = $('#problem-script').children().detach();

        new Vue({
            el: '#app',

            data: {
                item: {NAME: 'VueJS'},
            },
        });

       $('#problem-script').append(script);
</script>

</body>

</html>