How to pass JSON variable to Vue App + Components - javascript

I have a simple index.html file. It includes the Vue JS application and has the following code.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
<script>
const foo = {
bar: 100
};
</script>
<script src="main.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
Here's how the Vue JS app looks like.
main.js
import Vue from "vue"
import App from "./App.vue"
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount("#app")
App.vue
<template>
<div>
{{ config.bar }}
</div>
</template>
<script>
export default {
props: {
config: {
type: Object,
default: () => {},
},
}
}
</script>
I simply want to pass the const foo variable into the App.vue file. I already tried to pass it as a prop to the <div id="app" :config="foo"></div>, however it did throw an undefined error in App.vue.
How can I pass a JS variable from HTML into a Vue Application and to the component?

Related

Laravel inertia render blank page on IPhone device

Issue:
I have a laravel 9 app builded with vue3 , inertia-laravel , inertiajs and laravel-vite , it works perfetct on PC but when I try to open it in mobile device It show a blank page without any console log.
NB : when I toggle device toolbar to mobile in a navigator it works correctly but in real mobile no ( I tested on different navigators)
I can't fix this issue come from inertiajs\inertia-vue3 or inertiajs/inertia-laravel
When I render a blade view it works, just when I render inertia object
Versions:
inertiajs/inertia-laravel version: 0.6.3
#inertiajs/inertia version: 0.11.0
#inertiajs/inertia-vue3 version: 0.6.0
Codes:
web.php:
Route::get('/', function() {
return Inertia::render('WelcomeView');
});
app.js
import './bootstrap';
import '../css/main.css'
import { createPinia } from 'pinia'
import { useStyleStore } from '#/Stores/style.js'
import { darkModeKey } from '#/config.js'
import { createApp, h } from 'vue'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
import { InertiaProgress } from '#inertiajs/progress'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m'
import Notifications from 'notiwind'
import * as ConfirmDialog from 'vuejs-confirm-dialog'
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel'
const pinia = createPinia()
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(pinia)
.use(Notifications)
.use(ConfirmDialog)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});
InertiaProgress.init({
color: '#5136a8',
showSpinner:true
})
const styleStore = useStyleStore(pinia)
/* App style */
//styleStore.setStyle(localStorage[styleKey] ?? 'basic')
styleStore.setStyle()
/* Dark mode */
if ((!localStorage[darkModeKey] && window.matchMedia('(prefers-color-scheme: dark)').matches) || localStorage[darkModeKey] === '1') {
styleStore.setDarkMode(true)
}
WelcomeVue.vue (under pages folder) :
<template>
<div>
hellow world
</div>
</template>
and app.blade.php :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{!! csrf_token() !!}" />
<title inertia>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<!-- Scripts -->
#routes
#vite('resources/js/app.js')
#inertiaHead
</head>
<body class="font-sans antialiased">
#inertia
</body>
</html>

How do I display two components with vue.js?

this is my first post on here! Just need some small help with a problem. Thanks!
I just started learning vue.js and I'm trying to get my second components template to display on the page. For some reason, vue isn't rendering my second component, only the first. I'm thinking something is wrong within the index file with <education :data='data' />. Do I need to create a different <div id='app'> and put the second component within it?
app.js:
// Fetch handler
function fetchData(url) {
return fetch(url)
.then(checkStatus)
.then(res => res.json())
.catch(error => console.log('Error retrieving data', error))
}
// Response error handler
function checkStatus(res) {
if(res.ok) {
return Promise.resolve(res);
} else {
return Promise.reject(new Error(res.statusText));
}
}
(() => {
// Define components.
Vue.component('contact-info', {
props: ['data'],
template: `
<div>
<img v-bind:src='data.photo'>
<h1>{{ data.name }}</h1>
<p>{{ data.email }}</p><p>{{ data.phone }}</p>
<p v-if='data'>{{ data.links[0] }}</p><p v-if='data'>{{ data.links[1] }}</p>
</div>
`
});
Vue.component('education', {
props: ['data'],
template: `
<div>
<h3>Education</h3>
<div>
<h2></h2>
<p></p>
<p></p>
<div>
<div>
`
});
// Instantiate the app.
const app = new Vue({
el: '#app',
data() {
return {
data: ''
}
},
mounted() {
fetchData('https://wip.journeygroup.com/intern-api/joshbryant.json')
.then(jsonData => (this.data = jsonData))
}
});
})();
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Resume • Josh Bryant</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght#400;600;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./css/main.css">
</head>
<body>
<div id="app">
<contact-info :data='data' />
<education :data='data' />
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
<script src="./app/main.js"></script>
</body>
</html>
Your post shows app.js though the HTML looks for main.js, but I'll assume that's just a posting error since you've gotten some of it working.
The reason it doesn't work is because custom HTML components require a closing tag and cannot be self-closing. If you were using Vue CLI this would work anyway because the compiler would fix it, but not in a CDN implementation of Vue. The first component will work but everything that follows will be invalid HTML since the first component isn't closed properly.
This will work:
<div id="app">
<contact-info :data="data"></contact-info>
<education :data="data"></education>
</div>
A couple of other suggestions
As #tao mentioned in comments, it's confusing to call your variable data in the root component, and similarly to call your props data. These won't cause a problem but probably best to rename them all for clarity (so as not to be confused with the component's data option.)
There's an invalid bullet character in the HTML <title> tag which you can replace with &bullet;
It's best to use double quotes for your HTML attributes (the props use single quotes in your code)

Why is my Vue local component not appearing in the page?

I am learning Vue and messing around with examples in their guide. I am trying to register a sub-component locally inside a root vue component as described here. But it does not appear on my page. What am I missing ? app.components also returns undefined.
console.log('Loading my script');
var componentX = {
template: `<h3>Hello</h3>`
}
var app = new Vue({
el: '#vue-div',
template: `<div>Vue is working</div>`,
components: {
'component-x': componentX
}
})
console.log(app.components);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="vue-div">
<component-x></component-x>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="./js/test.js"></script>
</body>
</html>
The issue is, your main Vue has a template - this replaces any HTML inside the target element
So, you can either add the component-x inside the new Vue constructor template:
console.log('Loading my script');
var componentX = {
template: `<h3>Hello</h3>`
}
var app = new Vue({
el: '#vue-div',
template: `<div>Vue is working<component-x></component-x></div>`,
components: {
'component-x': componentX
}
})
console.log(app.$options.components);
<div id="vue-div"></div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="./js/test.js"></script>
Or you can remove the template: from the new Vie constructor and the existing content of the target element will be used
console.log('Loading my script');
var componentX = {
template: `<h3>Hello</h3>`
}
var app = new Vue({
el: '#vue-div',
components: {
'component-x': componentX
}
})
console.log(app.$options.components);
<div id="vue-div">
Vue is working
<component-x></component-x>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>

How to get DOM of a vue component without rendering it?

Suppose I have a simple Vue component like this:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
I don't want to render the component. I just want to pass the title somehow into blog-post component inside my script code, and get the DOM accordingly. For example, if I pass the title value Hello, then I expected the full DOM as <h3>Hello</h3>. I'll assign the DOM into a variable for using later.
One solution is to create a new Vue instance with only the target component, $mount it, and then get the outerHTML of its $el (root element):
Vue 2
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script>
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
const app = new Vue({
template: `<blog-post title="Hello world" />`
}).$mount()
console.log(app.$el.outerHTML)
</script>
Vue 3
In Vue 3, create an app instance, and call its mount() on a newly created <div>. The return value of mount() is the root component, which contains $el:
<script src="https://unpkg.com/vue#3.2.39/dist/vue.global.prod.js"></script>
<script>
const app = Vue.createApp({
template: `<blog-post title="Hello world" />`
})
app.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
const comp = app.mount(document.createElement('div'))
console.log(comp.$el.outerHTML)
</script>
If you want to get HTML of your component, you must to use ref attribute of parent element.
Try something like that:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<button #click="logComponentBlogPost">Log Component</button>
</div>
<div v-show="false" ref="BlogPost">
<blog-post title="Hello Word"></blog-post>
</div>
</div>
<script>
let BlogPost = Vue.component('BlogPost', {
props: ['title'],
template: '<h3>{{ title }}</h3>',
});
new Vue({
el: '#app',
components: { BlogPost },
methods: {
logComponentBlogPost() {
console.log(this.$refs.BlogPost.innerHTML);
},
},
});
</script>
</body>
</html>

Vue.js : "TypeError: Cannot set property props of #<Object> which has only a getter"

I am trying to instanciate a Vue component and I am getting the error :
[Vue warn]: Error in render: "TypeError: Cannot set property props of
#<Object> which has only a getter"
(found in <Root>)
I am also using the library vuedraggable but I presume that the problem is more a Vue problem than a vuedraggable one. Below is my code.
Here is draggable-list.vue
<template src="./draggable-list-component.html"></template>
<script src="./draggable-list.js"></script>
draggable-list.js
const draggable = require("vuedraggable");
module.exports = {
name: "draggable-list",
components: {
draggable
},
// properties which has been passed from the parent vue to the component
props: ["title", "elements"],
data() {
return {
isDragging: false,
};
},
methods: {
test() {
console.log("blou");
}
}
};
draggable-list-component.html :
<div id="draggable-list">
<draggable element="ul"
:list="elements">
<!-- TODO -->
</draggable>
</div>
My main.js calls then another js file :
require("./components/manager");
In the manager I instanciate my Vue instance :
const Vue = require("vue/dist/vue.common");
const draggableList = require("./draggable-list.vue");
let triggerLibrary;
triggerLibrary = new Vue({
el: "#draggable-list",
template: "<draggableList :title='title' :elements='triggerElements'
/>",
components: {draggableList},
data: {
title: "Trigger library",
triggerElements: [{name:"Trigger name", description:"Quick trigger
description"}]
}
});
And I am using it in my index.html like this :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>planner</title>
</head>
<body>
<div id="draggable-list">
<draggable-list></draggable-list>
</div>
</body>
Does anyone see what's wrong here?
As you are using CommonJS modules, try to require your Vue component that way :
const draggableList = require("./draggable-list.vue").default;

Categories