How can I get HTML element when importing Vue via CDN? - javascript

I'm using Vue 3.0.2 in my project, loaded via CDN as follows:
<head>
...
<script src="https://unpkg.com/vue#3.0.2"></script>
</head>
How can I access an HTML element in my Vue component when importing the library via CDN? I want to do the equivelant of what would be document.getElementById('element') in plain JS.
Related posts on here suggest that calling $this.el.querySelector should work for this, however I don't have access to that function. I suspect due to importing Vue via CDN?
My component looks as follows...
const app = Vue.createApp({
data() {
return {
// data members.
}
},
methods: {
// method definitions.
} });
app.mount('#app')
Thanks for any advice!

Try something like this
<script src="https://unpkg.com/vue#3.0.2"></script>
</head>
<body>
<div id="app">
<h1>{{ data }}</h1>
<input type="text" ref="input">
<button #click="log">LOG</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
data: 'any'
}
},
methods: {
log() {
console.log(this.$refs.input.value)
}
}
});
app.mount('#app')
</script>

Related

vue 3: Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of ""

Im trying to run simple vue3 application using options api. Here is code:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue#3/dist/vue.esm-browser.js"
}
}
</script>
<div id="app">
</div>
<!---->
<script type="module" src="main.js"></script>
</body>
</html>
main.js:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue:
<script>
export default {
// Properties returned from data() become reactive state
// and will be exposed on `this`.
data() {
return {
count: 0
}
},
// Methods are functions that mutate state and trigger updates.
// They can be bound as event listeners in templates.
methods: {
increment() {
this.count++
}
},
// Lifecycle hooks are called at different stages
// of a component's lifecycle.
// This function will be called when the component is mounted.
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button #click="increment">Count is: {{ count }}</button>
</template>
But getting this error in browser (i tried to set in apache different mimetypes for vue: text/html, text/javascript, module):
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
note: if I dont use vue files and just write app code in main.js it works fine:
const app = createApp({
// Properties returned from data() become reactive state
// and will be exposed on `this`.
data() {
return {
count: 0
}
},
// Methods are functions that mutate state and trigger updates.
// They can be bound as event listeners in templates.
methods: {
increment() {
this.count++
}
},
// Lifecycle hooks are called at different stages
// of a component's lifecycle.
// This function will be called when the component is mounted.
mounted() {
console.log(`The initial count is ${this.count}.`)
}
})
app.mount('#app')
What am I doing wrong? would appreciate clarification for this problem. Thanks.

How to create a Nuxt 3 Plugin from Vue 3 Plugin? (vue3-markdown-it)

I am new to Nuxt, and am attempting to turn the Vue plugin vue3-markdown-it into a Nuxt 3 plugin, but am receiving the following error:
[Vue warn]: Failed to resolve component: Markdown If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
What am I doing incorrectly?
Nuxt 3 Plugin Documentation
// md-plugin.client.ts
import Markdown from 'vue3-markdown-it'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(Markdown)
})
// index.vue
<template>
<main>
<ClientOnly>
<Markdown :source="content" />
</ClientOnly>
</main>
</template>
<script lang="ts" setup>
const { find } = useStrapi4()
const {
data: {
attributes: { content },
},
} = await find('homepage')
</script>
I had a similar problem on different plugin. I solved the problem by using component instead of plugin.
Create a new file under the components folder (Markdown.vue).
Add Markdown codes in this file:
<template>
<Markdown :source="content" />
</template>
<script lang="ts" setup>
import { defineProps } from "vue";
import Markdown from 'vue3-markdown-it';
defineProps({
content: { type: any }
});
</script>
You can customize this code.
Delete plugin file (md-plugin.client.ts)
Now <Markdown :source="content" /> will see our generated Markdown component. It worked me.

Register Vue.js component dynamically from a string

I have a CRUD that enables me to write Vue.js component's code in the textarea like:
<template>
<div><p class='name-wrapper'>{{ model.name }}</p></div>
</template>
<script>
module.exports = {
name: 'NameWrapper',
props: ['model']
}
</script>
<style lang='sass'>
.name-wrapper
color: red
</style>
Then in other component, I fetch this data and want to register it as a dynamic/async, custom component like:
<template>
<component :is='dynamicName' :model='{name: "Alex"}'></component>
</template>
<script>
import httpVueLoader from 'http-vue-loader'
import Vue from 'vue'
export default {
name: 'DynamicComponent',
props: ['dynamicName', 'componentDefinitionFromTextareaAsString'],
beforeCreate: {
// I know that as a second parameter it should be an url to the file, but I can't provide it, but I would like to pass the contents of the file instead there:
httpVueLoader.register(Vue, this.$options.propsData.componentDefinitionFromTextareaAsString)
// I was trying also:
Vue.component(this.$options.propsData.dynamicName, this.$options.propsData.componentDefinitionFromTextareaAsString)
}
}
</script>
As far as I know, httpVueLoader needs the url to the .vue file instead - is there a way to pass there the code itself of the component?
I am aware that passing and evaluating <script></script> tag contents can cause security issues, but I really need to do it that way.
I've read also about Vue.js compile function, but that works only for templates, not the code of the component (so the script tags again).
Is it even possible to achieve such functionality in Vue.js?
It should be possible to use a data: URI with http-vue-loader, like this:
const vueText = `
<template>
<div class="hello">Hello {{who}}</div>
</template>
<script>
module.exports = {
data: function() {
return {
who: 'world'
}
}
}
<\/script>
<style>
.hello {
background-color: #ffe;
}
</style>
`
const MyComponent = httpVueLoader('data:text/plain,' + encodeURIComponent(vueText))
new Vue({
el: '#app',
components: {
MyComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/http-vue-loader#1.4.1/src/httpVueLoader.js"></script>
<div id="app">
<my-component></my-component>
</div>
If that doesn't work for some reason (maybe because one of your target browsers doesn't support it) then you could get it working by patching httpRequest. See https://www.npmjs.com/package/http-vue-loader#httpvueloaderhttprequest-url-. The documentation focuses on patching httpRequest to use axios but you could patch it to just resolve the promise to the relevant text.

VueJS data() not working

I am trying to make a VueJS app but I am failing even with the simplest examples.
I am using Laravel 5.3 with pre-built support for VueJS (version 1, I tried version 2 as well).
Here is my Example.vue component
<template>
<div class="profile">
{{ name }}
</div>
</template>
<script>
export default {
data () {
return {
name: 'John Doe'
}
}
}
</script>
And here is the main code
Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
el: '#app'
});
This is the error that shows up everytime in console:
[Vue warn]: Property or method "name" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in component )
Any ideas whats wrong?
Thanks
In your script tags instead of export default use:
module.exports = {
data() {
return { counter: 1 }
}
}
This should work for you
Call the component inside your template
Vue.component('example', {
template: `<div class="profile">{{ name }}</div>`,
data () {
return {
name: 'John Doe'
}
}
})
const app = new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app"><example></example></div>
The problem is that you are trying to load the component 'example' from that file but you didn't give a name to it. You should use:
<script>
export default {
name: 'example',
data() {
return {
name: 'John Doe'
}
}
}
</script>
Or load the component the following way (not sure if extension .vue is needed):
require('./exmaple').default();
If you are using Babel you can also load the components without giving them a name using this syntax:
import Example from ./example
Also checkout this post to get some more info in case you use Babel

How can I bind the html <title> content in vuejs?

I'm trying a demo on vuejs. Now I want the html title to bind a vm field.
The below is what I tried:
index.html
<!DOCTYPE html>
<html id="html">
<head>
<title>{{ hello }}</title>
<script src="lib/requirejs/require.min.js" data-main="app"></script>
</head>
<body>
{{ hello }}
<input v-model="hello" title="hello" />
</body>
</html>
app.js
define([
'jquery', 'vue'
], function ($, Vue) {
var vm = new Vue({
el: 'html',
data: {
hello: 'Hello world'
}
});
});
But the title seemed not bounded, how to make it work?
There are essentially two ways to solve it.
Use an existing Package
For example, vue-meta:
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
metaInfo: {
// if no subcomponents specify a metaInfo.title, this title will be used
title: 'Default Title',
// all titles will be injected into this template
titleTemplate: '%s | My Awesome Webapp'
}
}
</script>
Create your own Component
Create a vue file containing:
<script>
export default {
name: 'vue-title',
props: ['title'],
watch: {
title: {
immediate: true,
handler() {
document.title = this.title;
}
}
},
render () {
},
}
</script>
Register the component using
import titleComponent from './title.component.vue';
Vue.component('vue-title', titleComponent);
Then you can use it in your templates, e.g.
<vue-title title="Static Title"></vue-title>
<vue-title :title="dynamic.something + ' - Static'"></vue-title>
You can do it with 1 line in the App.vue file, like this:
<script>
export default {
name: 'app',
created () {
document.title = "Look Ma!";
}
}
</script>
Or change the <title> tag content in public/index.html
<!DOCTYPE html>
<html>
<head>
<title>Look Ma!</title> <!- ------ Here ->
</head>
...
This answer is for vue 1.x
using requirejs.
define([
'https://cdn.jsdelivr.net/vue/latest/vue.js'
], function(Vue) {
var vm = new Vue({
el: 'html',
data: {
hello: 'Hello world'
}
});
});
<!DOCTYPE html>
<html id="html">
<head>
<title>{{ hello }}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js" data-main="app"></script>
</head>
<body>
{{ hello }}
<input v-model="hello" title="hello" />
</body>
</html>
you can do it like this using the ready function to set the initial value and watch to update when the data changes.
<html>
<head>
<title>Replace Me</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input v-model="title">
</div>
<script>
new Vue({
el: '#app',
ready: function () {
document.title = this.title
},
data: {
title: 'My Title'
},
watch: {
title: function (val, old) {
document.title = val
}
}
})
</script>
</body>
</html>
also i tried this based on your original code and it works
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input v-model="title">
</div>
<script>
new Vue({
el: 'html',
data: {
title: 'My Title'
}
})
</script>
</body>
</html>
Just to chime in here. I have read that VueJS wants nothing to do with the meta stuff so I would do such things outside of the "VueJS" realm.
Basically make a plain vanilla js service like below. Here you could add all the functions to handle the meta data stuff such as the Open Graph data.
meta.js
export setTitle(title) {
document.title = title
}
Now we can import the service in main and then provide it to any component in the app who wants it. I could even use my meta service in other projects too which use different frameworks like React or Angular. Portability is super cool!
main.js
import meta from './meta'
new Vue({
router,
render: h => h(App),
provide: {
meta: meta
}
}).$mount('#app')
Here the component injects the meta service it wants to use.
someView.vue
export default {
name: 'someView',
inject: ['meta'],
data: function() {
returns {
title: 'Cool title'
}
},
created: function() {
this.meta.setTitle(this.title);
}
}
This way the meta service is decoupled from the app because different parent components can provide different versions of the meta service. Now you can implement various strategies to see which one is right for you or even different strategies per component.
Basically the inject walks up the component hierarchy and takes the meta service from the first parent who provides it. As long as the meta service follows a proper interface, you're golden.
Decoupling with DI is super cool 😃
Title and meta tags can be edited and updated asynchronously.
You can use state management, create a store for SEO using vuex and update each part accordingly.
Or you can update the element by yourself easily
created: function() {
ajax().then(function(data){
document.title = data.title
document.head.querySelector('meta[name=description]').content = data.description
})
}
If you are using Vuex and want <title> to be part of your application state, then:
create a pageTitle state variable in Vuex
map the state to the template using mapState()
watch it in template, probably add immediate: true to trigger the watcher right away
in watcher, document.title = pageTitle
This will allow you to manage title with Vuex and keep them in sync. I found it useful for SPAs.
By doing this you don't have to mess with your original HTML template, as most of the time Vue root template resides inside <body>.
This is for Vue 2.x.
router.beforeEach((to, from, next) => {
let mohican = to.path; if (mohican == '/') mohican = 'Home'
document.title = mohican.replace('/','');
next();
return;
});
I have an application toolbar component which is common for all pages of my SPA website and is nested in App.vue. In every page I update my common toolbar title in the created hook of the page using Vuex store:
//in every page.vue
created() {
this.$store.commit('toolBar', { pageTitle: this.pageTitle, ... })
},
To automatically update the website title (along with the toolbar title) I use this mutation in the store:
//store.js
toolBar(state,val){
document.title = val.pageTitle
state.toolBar = val
},
Similarly, I use the same mechanism to update e.g. SEO metadata
just pass
:title="data.name"

Categories