Vuejs and drupal - javascript

I'm working on a Drupal project where we compile the js and sass of the theme with webpack. As we are moving in a near future to other backend(Laravel), and the idea is to use vuejs on front-end. So it seems to us a good idea, in meanwhile, start using vuejs in some pages and components, so we could start learn it about it. I have experience with angular and react but none with vue. I add it vue, the vue-loader, etc, but seems dificult to make it work and I'm not sure which could be the best way to implement/add vuejs in this escenario. Any recomendation or link will we very helpful.

Introduction
Vue is good choice because of two reasons in your case:
It is simplest to learn than Angular and React
It is progressive - it means you can easy use it only in constrained part of your existing project.
If you look at dock of life cycle of Vue instance
https://v2.vuejs.org/v2/guide/instance.html
You will see there are some options of create template and connect it with instance of vue.
a) by "el" option - selecting existing element from dom
b) by template option
including template as a string
selecting template by id of script with type text/x-template
You can use Vue instantly after page load or mount it later so you have flexibility.
Examples
I understood your question is about simplest way to integrate vue with drupal. I think that these examples of use Vue on simple html page will help you.
Simplest way
Simplest way is use el option and load Vue from cdn. ( remember about change cdn to minified on production )
<style>
[v-cloak] {
display: none;
}
</style>
<div id="app" v-cloak>
<h1>{{ heading }}</h1>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script>
new Vue({
el: "#app",
data: { heading: "Hello World" }
});
</script>
By using text/x-template
<div id="app"></div>
<script id="app-template" type="text/x-template">
<div>
<h1>{{heading}}</h1>
</div>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script>
let v = new Vue({
template: `#app-template`,
data: { heading: "Hello World" }
});
v.$mount();
document.querySelector("#app").appendChild(v.$el);
</script>

Related

Is there a way to suppress VueJS "Cannot find element" warning

I have a script file that contains two Vue apps, that I use across multiple django templates. Apps are vue1, and vue2, with el: #vue1-app, and el: #vue2-app.
On one particular page I only use one of those apps, not both. So my html contains only <div id="vue1-app"></div> and not both divs with vue1-app and vue2-app ids. Therefore Vue logs warnings "Cannot find element: #vue2-app".
Is there a way to suppress those warning or any other bypass?
Thank you!
I'm not a Django developer, but I think maybe you can just split out them into two files or add a condition like:
const vueApp = document.querySelector('#vue-app')
if(vueApp){
// mount the app
}
Vue warnings are not emitted in the production (minified) version of Vue, so you could load vue.min.js:
new Vue({
el: '#does_not_exist',
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<p>{{ message }}</p>
</div>

Clean separation of view and code with Vue using templates

I hope this is a trivial problem for someone and I am just missing a tiny magic piece of code: I am migrating from knockout.js to vue.js and I am wondering if there is a simple way to use templates the same way in vue.js as in knockout.js. The following code snipped uses knockout.js and works as expected:
function viewmodel() {
this.person = ko.observable(new person());
}
function person() {
this.first = ko.observable('hello');
this.last = ko.observable('world');
}
ko.applyBindings(new viewmodel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/html" id="person-template">
<p data-bind="text: first"/>
<p data-bind="text: last"/>
</script>
<div>
<div data-bind="template: { name: 'person-template', data: person }"/>
</div>
However, I can't figure out how to achieve the same in vue.js. I am aware of components in vue.js and all that, but I want to keep the structure as it is and not put more view-specific code into my JavaScript files. (I am already not happy with the el: '#app', but that's my least concern at the moment) Of course this code doesn't work:
var app = new Vue({
el: '#app',
data: {
person: {
first: 'hello',
last: 'world'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script type="text/x-template" id="person-template">
<p v-text="first"/>
<p v-text="last"/>
</script>
<div id="app">
<div><!-- how? is this even possible? --></div>
</div>
Is there a way to get this working by only changing the HTML part of code? Preferably only the <div>-Element.
So this is not quite only changing the template, but a single additional property can work here. Specify a template in the Vue.
var app = new Vue({
el: '#app',
template: "#person-template",
data: {
person: {
first: 'hello',
last: 'world'
}
}
});
I also modified your template slightly because a template for any Vue or component requires one root element. I recommend you do not use self closing tags in Vue as I've seen this cause issues. Vue requires valid HTML5 and self closing tags are not valid HTML5.
<script type="text/x-template" id="person-template">
<div>
<p v-text="person.first"></p>
<p v-text="person.last"></p>
</div>
</script>
Working example.
Additionally, a very common way of specifying templates in this fashion, without using the kind of hacky script tag is to use the HTML5 template element.
<template id="person-template">
<div>
<p v-text="person.first"></p>
<p v-text="person.last"></p>
</div>
</template>
Finally, knowing you are slightly unhappy with the el:"#app" You can leave the el property out of the Vue definition and mount the Vue on an element of your choice using the $mount method. So if you wanted to go completely hog wild on separation of concerns you could do something like this.
const renderer = Vue.compile(document.querySelector("#person-template").innerHTML)
const example = {
data: {
person: {
first: 'hello',
last: 'world'
}
}
}
const app = new Vue(Object.assign({}, example, renderer))
app.$mount("#app")
Example.
I should mention nobody really does that (compiling their templates manually, it's common to use $mount) that I'm aware of. It also depends on using the version of Vue that includes the compiler (which you might not have if you are using webpack or some other build tool for example). You could also do something like this:
const app = new Vue(Object.assign({}, example, {template: "#person-template"}))
app.$mount("#app")
which would basically allow you to specify a template, a component, and a place to mount it all separately.

Vue.js 2.0: Apply vue component rendered using v-html, compile the markup

I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>

Mixing Vue.js into existing SSR website?

Is it possible/viable to use Vue to create components that get instantiated onto custom tags rendered by e.g. a PHP application? Some kind of "custom elements light"?
It "works" if I mount the Vue instance onto the page root element, but - as far as I understand - Vue uses the whole page as a template in this case. And I imagine this could be a performance issue and cause trouble if other javascript code messes with the DOM.
The goal is to progressively replace legacy jQuery code. Is there an common approach for this problem?
Edit: Nesting these components is also a requirement. A simple example would be nested collapsibles.
Edit 2: Some fiddles to illustrate the problem.
A working solution based on riot.js:
https://jsfiddle.net/36xju9sh/
<div id="page">
<!-- This is supposed to show "This is a test." twice. -->
<test>
<test></test>
</test>
</div>
<script type="riot/tag">
<test>
<p>This is a test.</p>
<yield/>
</test>
</script>
<script>riot.mount('*')</script>
Two approaches using Vue.js:
https://jsfiddle.net/89ejjjsy/1/
HTML:
<div id="page">
<!-- This is supposed to show "This is a test." twice. -->
<test>
<test></test>
</test>
</div>
<script type="text/x-template" id="test-template">
<p>This is a test.</p>
<slot></slot>
</script>
Javascript:
Vue.config.ignoreCustomElements = ['test'];
// This won't hit the nested <test> element.
new Vue({
el:'test',
template: '#test-template',
});
// This does, but takes over the whole page.
Vue.component('test', {
template: '#test-template',
});
new Vue({
el: '#page',
});
You do not have to use whole page as Vue instance scope. It is more than ok to for example, use #comments-container as scope for comments component which will be nested somewhere on your page.
You can have multiple VueJS instances on one page.

How to use components in v-html

I am trying to use components in v-html.
I want to get html from own API, then I will show that.
Here is my code.
HTML:
<!-- app -->
<div id="app">
<span v-html="html"></span>
<badge></badge>
<span v-html="html2"></span>
<partial name="my-partial"></partial>
<span v-html="html3"></span>
</div>
Javascript:
Vue.component('badge', {
template: '<span class="component-tag">This is component</span>',
})
Vue.partial('my-partial', '<p>This is a partial!</p>')
// start app
new Vue({
el: '#app',
data: {
html: '<p>THIS IS HTML</p>',
html2: '<badge></badge>',
html3: '<partial name="my-partial"></partial>'
}
})
https://jsfiddle.net/9w3kz6xm/4/
I tried partials because Vue document says " If you need to reuse template pieces, you should use partials."
It does not work. Maybe I am making mistake, I don't know what is a mistake.
Thank you.
Pretty sure Vuejs makes it very hard to directly use external html. v-html will simply replace the html content and therefore will not execute any directive. The purpose of it is to avoid XSS attacks as documented here: https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML
Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML interpolation on trusted content and never on user-provided content.
If you really need to use external html, it is possible to use Vue.compile() documented here: https://v2.vuejs.org/v2/api/#Vue-compile
A working example can be found here: https://jsfiddle.net/Linusborg/1zdzu7k1/
and its related discussion can be found here: https://forum.vuejs.org/t/vue-compile-what-is-staticrenderfns-render-vs-staticrenderfns/3950/7

Categories

Resources