How to call Vue 3 method from outside the app - javascript

what I want is to call a method, declared in vue 3 app form outside the component of the page. So what I did sofar:
App.vue
<script setup>
function test(){
console.log('test');
}
</script>
vue.js
import { createApp } from 'vue'
import App from 'App.vue'
window.app = createApp(App).mount('#app')
index.html
<div id="app"></app>
<script src="app.js"></script>
<script>
fuction callTest(){
window.app.test() // <-- this returns undefined
}
</script>
however it worked with vue2. Any idea how to get it work with vue3?

You need to use defineExpose inside in the first file in order to use it outside the component:
<script setup>
function test(){
console.log('test');
}
defineExpose({
test
})
</script>

Related

v-model throws ReferenceError only in Production

I have the following View in Vue:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>
<script>
export default {
data() {
return {
text: ""
};
},
components: { Overwrite: Overwrite },
};
</script>
Everything works perfectly fine when I start the application with npm run dev.
However, when I build the app for production and run it, I get the following error as soon as I type anything into the textarea:
index.57b77955.js:3 Uncaught ReferenceError: text is not defined
at HTMLTextAreaElement.t.onUpdate:modelValue.s.<computed>.s.<computed> [as _assign] (index.57b77955.js:3:1772)
at HTMLTextAreaElement.<anonymous> (vendor.31761432.js:1:53163)
I also have other form elements that show the exact same behaviour.
You can use a maximum of 1 × <script> tag and a maximum of 1 × <script setup> per vue component.
Their outputs will be merged and the object resulting from merging their implicit or explicit exports is available in <template>.
But they are not connected. Which means: do not expect any of the two script tags to have visibility over the other one's imports.
The worst part is that, although the first <script setup> does declare Ovewrite when you import it (so it should become usable in <template>), the second one overwrites it when you use components: { Overwrite: Overwrite }, because Overwrite is not defined in the second script. So your components declaration is equivalent to:
components: { Overwrite: undefined }
, which overwrites the value already declared by <script setup>.
This gives you two possible solutions:
Solution A:
<script>
import Overwrite from "../components/Overwrite.vue";
export default {
components: {
Overwrite
},
// you don't need `data` (which is Options API). use `setup` instead
setup: () => ({
text: ref('')
})
}
</script>
Solution B:
<script setup>
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
Or even:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<script>
export default {
data: () => ({ text: "" })
};
</script>
Can you try using only the setup script tag? Using it only for imports this way doesn't make sense. If you import a component in setup script tags you don't need to set components maybe the issue is related to that.
Also you could try the full setup way then:
<script setup>
import { ref } from 'vue'
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>

How to target elements inside <script setup> of Vue 3 component?

How can I target the element with test class inside this vue component:
<template>
<div id="test" class="test">
</div>
</template>
<script setup>
console.log(document.querySelector('.test'))
</script>
This component is imported into another component. The console outputs null.
the component is not rendered before that script is run
You want to wait until it is mounted
<template>
<div id="test" class="test">
</div>
</template>
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
console.log(document.querySelector('.test'))
});
</script>
In general, there's hardly ever any need to use DOM methods like that in vue though - I won't say never, because I know as soon as I say that, someone will come up with a valid reason to
I never have though
The best practice is to use ref instead of DOM manipulations methods, and also use onMounted hook to check the element as mentioned in the following example:
<script setup>
import { ref,onMounted } from 'vue'
const input =ref()
onMounted(()=>{
input.value.focus()
})
</script>
<template>
<input ref="input">
</template>
you can use onMounted for target the element inside vue component:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(document.querySelector('.test'));
})
</script>
but I recommend use ref instead of DOM manipulations method:
<script setup>
import { ref, onMounted } from 'vue'
const el = ref()
onMounted(() => {
el.value // <div>
})
</script>
<template>
<div ref="el"></div>
</template>
docs: https://vuejs.org/api/composition-api-lifecycle.html#onmounted

Laravel 8 pass data from blade to Vue 3

I try to pass data from Laravel blade to Vue with props and I get undefined in console.
I tried also
<example-component :course="{{ $course }}"></example-component>
<example-component course="{{!! $course !!}}"></example-component>
index.blade.php
#extends('layouts.app')
#section('content')
<div id="app">
</div>
<example-component course="{{ $course }}"></example-component>
<script src="{{asset('js/course/app.js')}}"></script>
#endsection
app.js
import { createApp } from 'vue';
import App from './components/App.vue'
import router from '../../router'
createApp(App).use(router).mount("#app")
App.vue
<template>
{{ course }}
</template>
<script>
export default {
props: ['course'],
mounted() {
console.log(this.course)
}
}
</script>
The best practice is to make you component call an API to get the course.
If it's not worth an API try to pass the $course as attr to your #app element. Then you can get your course with getAttribute
<div id="app" course="{{json_encode($course)}}"></div>
in vue.js:
document.getElementById('app').getAttribute('course')

How to call a Vue method from main.js in Vue.js file

Why am I not able to call my method in App.vue?
Isn't there the div id='App' reference in the App which should make it possible to call methods in there?
Main.js
new Vue({
render: h => h(App),
methods:{
gesamt:function () {
return 'Hello';
}
}
}).$mount('#app')
App.vue
<template>
<div id="app">
<p>{{gesamt()}}</p>
<navbar></navbar>
<ausgaben></ausgaben>
</div>
</template>
<script>
export default {
name:"App",
}
</script>
If you really want to add a "global" method to your VueJs app you can add it to Vue itself like this:
import {logEngagement} from "./helpers/logEngagement"
Vue.prototype.$logEngagement = logEngagement;
you can use it in component.vue like this:
<script>
export default {
mounted() {
this.$logEngagement.log("this");
}
}
<script>

How to create and render a React Component after babelify/transpiling?

I have a hello world react component that is written in JSX, transpiled with babel, and then included in the hello.html template of a Flask app. What I have working is creating and rendering the component before transpiling as such:
const hello = <Hello name="world" />;
ReactDOM.render(hello, document.getElementById('hello'));
How can I do those two steps in a <script> tag in my hello.html template? My goal is to be able to pass that name variable from the template to the component and then render it.
A little more context:
The JSX hello.js looks like this:
import React from 'react';
import ReactDOM from 'react-dom'
import { render } from 'react-dom'
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div>Hello {this.props.name}!!!</div>
)
}
}
//The following works:
//const hello = <Hello name="world" />;
//ReactDOM.render(hello, document.getElementById('hello'));
hello.html looks like this:
<html>
<head>
</head>
<body>
<div>ASDF</div>
<div id="hello"></div>
</body>
{# The following line is a post babelify (transpiled) hello.js #}
<script type="text/javascript" src="{{ url_for('static', filename='js/hello.js') }}"></script>
<script type="text/javascript">
{#
What goes here? The code in the above section does not work.
The transpiled code defines a "var Hello = /*#__PURE__*/ function (_React$Component) { ...".
const hello = Hello(); does not throw an error, but also does not render or pass an argument.
hello.render(); is also something that I have tried, along with arguments for div/id to render in and name.
#}
</script>
</html>
Correction: Calling Hello() does not throw an error if the script is text/babel, in which case the script probably isn't doing anything.
The Flask route looks like this:
#app.route(u'/')
def index():
return render_template(u'hello.html', name="universe")
Two ways you can pass variables from your server application to react component:
Use the html data-variable prop.
Create a global variable. Something like window.variable
Then you should be able to access variable as a props like props.variable in your react-component.
My recommended approach I would take is to use a bundler such as SystemJS (version 2), and you will have something like the following:
<!DOCTYPE html>
<html>
<head>
<script src="node_modules/core-js-bundle/minified.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script type="systemjs-importmap" src="systemjs.imports.json"></script>
<script src="node_modules/systemjs/dist/system.min.js"></script>
<script src="node_modules/systemjs/dist/extras/named-exports.min.js"></script>
<script>
System.import('../.playground/index.js').catch(function (err) { console.error(err); });
</script>
</head>
<body>
<div>ASDF</div>
<div id="hello"></div>
</body>
</html>
And index.js will look something like this
ReactDOM.render(
(< Hello/>),
document.getElementById('app')
);
Then your systemjs-importmap will look like this
{
"imports": {
"react": "../node_modules/react/umd/react.production.min.js",
"react-dom": "../node_modules/react-dom/umd/react-dom.production.min.js",
// ... other named exports you want to add like the Hello component here
}
}

Categories