I worked with Vue2, but I recently try Vue 3.
I have simple problem:
<input ref="myinput" />
<button #click="submitData" />
I want to set "focus" on "myinput", inside function "submitData".
In Vue 2 it is simple (this.$refs ...), but in Vue 3, they made it complicated.
I saw example with "setup", but is no use for me + I think you can only access "value" from element.
Is there any way to execute "focus" on on element inside methods?
You are still able to do the same thing using Vue 3, but if you work with composition api there's some difference :
Options API :
const {
createApp
} = Vue;
const App = {
data() {
return {
}
},
methods: {
submitData() {
this.$refs.myinput.focus()
}
},
mounted() {
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
Vue 3 app
<input ref="myinput" />
<button #click="submitData">
Submit
</button>
</div>
composition API:
const {
createApp,
ref,
onMounted,
} = Vue;
const App = {
setup() {
const myinput = ref(null)
function submitData() {
myinput.value.focus()
}
return {
myinput,
submitData
}
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
Vue 3 app
<input ref="myinput" />
<button #click="submitData">
Submit
</button>
</div>
In case someone comes to this question looking for a way to set the autofocus of a specific element in Vue3, you can achieve it using a Vue Custom Directive
const { createApp, onMounted } = Vue;
const app = createApp({})
// Register a global custom directive called `v-focus`
app.directive('focus', {
// When the bound element is mounted into the DOM...
mounted(el) {
// Focus the element
el.focus()
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<input />
<input v-focus />
<input />
</div>
In some cases when the input is hidden under a v-show or v-if it is necessary to do a nextTick for the focus to work.
<span
v-show="!editMode"
#click="handleEditionMode"
>
{{ content }}
</span>
<input
v-show="editMode"
ref="input"
v-model="content"
aria-describedby="item-content"
name="content"
type="text"
tabindex="0"
#focusout="editMode = false"
#keydown.enter="editMode = false"
/>
const input = ref(null),
editMode = ref(false);
const handleEditionMode = () => {
editMode.value = true;
nextTick(() => {
input.value.focus();
});
};
Easiest answer I found is missing here
<input type="text" autofocus />
I was trying to select a specific input upon loading the form component.
The above examples were not useful, so I figured it out myself.
This is far simpler, IMHO. Add 1 ref tag and 1 line of code in the mounted hook.
Place a ref tag on the item you'd like to focus. Here I named it "formStart" but you can name yours whatever you like.
<form #submit.prevent="createNewRoute">
<label for="code">Code</label>
<input v-model="code" id="code" type="text" ref="formStart" /> <!-- REF TAG HERE -->
<label for="type">Type</label>
<input v-model="type" id="type" type="text" />
/* Other inputs hidden for simplicity */
<button type="submit">Add Route</button>
</form>
Reference that ref tag in the mounted hook and focus() it.
<script>
export default {
/* Other options hidden for simplicity */
mounted() {
this.$refs.formStart.focus(); // FOCUS ELEMENT HERE
},
};
</script>
Another vanilla solution is:
document.getElementById("code")?.focus()
to be called on onMounted
<div
v-if="openmodal"
tabindex="0"
v-focus
#keydown.right="() => nextimage(1)"
>
</div>
i used methods of #webcu and added tabindex="0" it's work!
Related
I'm stuck at this error that I get only if I run my laravel + vuejs in build mode.
Ihave a custom component with 3 input and I'm using it in a Quasar dialog. When I start typing inside one of these 3 inputs, it give me the error I wrote in the post title.
See code and details.
I have a custom component file (EditObjectTranslation_Singleline.vue)
<template>
<q-dialog ref="dialogRef" #hide="onDialogHide">
<q-card class="q-dialog-plugin">
<!--
...content
... use q-card-section for it?
-->
<q-card-section>
<div class="text-h6">{{ titolo }}</div>
<q-form #submit.prevent="invia_form">
<div>
<q-input outlined v-model="contenuto_campo" label="Contenuto CAMPO" class="mt-4 block w-full" :disable="true"/>
</div>
<div>
<q-input outlined v-model="contenuto_lingua_default" label="Contenuto Lingua IT" class="mt-4 block w-full" :disable="true"/>
</div>
<div>
<q-input outlined v-model="contenuto_translated" :label="label_translated" class="mt-4 block w-full" :maxlength="max_lunghezza"/>
</div>
</q-form>
</q-card-section>
<!-- buttons example -->
<q-card-actions align="right">
<q-btn color="primary" label="OK" #click="onOKClick" />
<q-btn color="primary" label="Cancel" #click="onDialogCancel" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
import { useDialogPluginComponent } from 'quasar'
import { computed } from '#vue/reactivity'
const props = defineProps({
contenuto_campo: '',
contenuto_lingua_default: '',
contenuto_translated: '',
lingua: '',
max_lunghezza: 0
})
const label_translated = computed (() => {
return "Contenuto tradotto " + props.lingua
})
defineEmits([
// REQUIRED; need to specify some events that your
// component will emit through useDialogPluginComponent()
...useDialogPluginComponent.emits
])
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
// dialogRef - Vue ref to be applied to QDialog
// onDialogHide - Function to be used as handler for #hide on QDialog
// onDialogOK - Function to call to settle dialog with "ok" outcome
// example: onDialogOK() - no payload
// example: onDialogOK({ /*...*/ }) - with payload
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
// this is part of our example (so not required)
function onOKClick () {
// on OK, it is REQUIRED to
// call onDialogOK (with optional payload)
onDialogOK(props)
// or with payload: onDialogOK({ ... })
// ...and it will also hide the dialog automatically
}
</script>
Then, I want to use the previous component as a custom component in a quasar dialog.
So the page code where I'm using the previous custom components follows
<script setup>
import AuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { Inertia } from '#inertiajs/inertia';
import { useQuasar } from 'quasar';
import { ref } from 'vue';
import { computed } from '#vue/reactivity';
import { Link } from '#inertiajs/inertia-vue3';
import CustomComponent from '#/Components/EditObjectTranslation_Singleline.vue';
import axios from 'axios';
const $q = useQuasar()
const props = defineProps({
catalogues: Array,
lingue: Array,
field2betranslated: Number
})
const $qTranslate = useQuasar()
function onTranslateObject (pLingua) {
if (selected.value.length > 0 ) {
axios
.get('/api/getTraduzioni/'+ pLingua + '/' + selected.value[0].resource_id)
.then((response) => {
$q.dialog({
component: CustomComponent,
componentProps: {
titolo: 'Traduci contenuti:',
contenuto_campo: selected.value[0].name,
contenuto_lingua_default: response.data.originale,
contenuto_translated: response.data.tradotto,
lingua: pLingua,
max_lunghezza: 64,
resource_id: selected.value[0].resource_id
// ...more..props...
}
}).onOk((formData) => {
// console.log('>>>> OK')
translateObject(formData)
}).onOk(() => {
// console.log('>>>> second OK catcher')
}).onCancel(() => {
// console.log('>>>> Cancel')
}).onDismiss(() => {
// console.log('I am triggered on both OK and Cancel')
})
})
} else {
$q.dialog({
title: 'Attenzione',
message: 'Devi selezionare una riga'
})
}
}
</script>
<template>
<Head :title="$t('cataloghi.pagetitle') " />
<AuthenticatedLayout>
<div class="q-py-xl">
<!-- Pulsanti traduzioni -->
<div class="w-full flex justify-end space-x-2 mb-4">
<q-btn v-for="lingua in lingue" :label="lingua.codiceIso" :key="lingua.codiceIso" #click="onTranslateObject(lingua.codiceIso)" icon="language" color="whte" text-color="text-grey-7" />
</div>
</div>
</AuthenticatedLayout>
</template>
When I open the dialog and try to input in the "contenuto_translated" q-input I get this error:
"ReferenceError: contenuto_translated is not defined"
Surely I'm missing something.
Please help me.
I'm doing a simple blog app to practice vue.js. I'm using composition API. I have stored data that get filled in in a form. This data I want to print out in another component homePosts where you can see the written blogpost with writer, headline and blogtext. I have used v-model, stored data to localStorage, in homePosts I have used v-for and {{ }} syntax to get data. But nothing shows in homePosts.
Can someone please see what im missing.
writePost.vue
<template>
<div>
<form class="form">
<label for="writer">Writer name: </label>
<input v-model="newWriter" type="text" max="500" />
<br />
<label for="img">Select image:</label>
<input type="file" id="img" name="img" accept="image/*" />
<br />
<label for="headline">Headline </label>
<input v-model="newHeadline" type="text" max="500" />
<label>Your blogtext: </label>
<textarea v-model="newNote" name="" id="" cols="30" rows="30"></textarea>
<button type="submit" #click="addNote" class="button"><router-link to="/homePosts" class="link">Post blog</router-link></button>
</form>
</div>
</template>
<script setup>
import { ref } from "vue";
const newNote = ref("");
const newWriter = ref("");
const newHeadline = ref("");
const notes = ref([]);
const addNote = () => {
notes.value.push({
id: Math.floor(Math.random() * 1000000),
text: newNote.value,
writer: newWriter.value,
headline: newHeadline.value,
});
addLocalStorage(notes)
};
const addLocalStorage = (notes) => {
localStorage.setItem("notes", JSON.stringify(notes))
JSON.parse(localStorage.getItem("notes"));
}
</script>
homePosts.vue
<template>
<div class="post-container">
<h1>Blog Posts</h1>
<div class="post-mini-container" >
<div class="post" v-for="note in notes" :key="note.id">
<!-- <img class="img-post" src="#/assets/person1.jpg"> -->
<p class="writer"> {{ note.writer }}</p>
<p class="headline"> {{ note.headline }}</p>
<p class="blog-text" > {{ note.text }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'homePosts'
}
</script>
You need to start your ref already parsing the existing items in your localStorage.
const notes = ref(JSON.parse(localStorage.getItem('notes') ?? '[]');
Or better yet, use a computed getter/setter:
const notes = computed({
get: () => JSON.parse(localStorage.getItem('notes') ?? '[]'),
set: (value) => {
localStorage.setItem('notes', JSON.stringify(value))
}
});
Or even better, take a look at vueUse/useLocalStorage 🎉
there are two approaches that you can follow, "event bus" or "pinia / vuex".
i'll explain how you can implement event bus
(you can check this post for inspiration: https://medium.com/#certosinolab/using-event-bus-in-vue-js-3-425aae8c21a6)
Add global event bus
install mit: npm install --save mitt
go to your main.ts / main.js and add the global property
import mitt from 'mitt';
const dispatcher = mitt();
const app = createApp(App);
app.config.globalProperties.dispatcher = dispatcher;
app.mount('#app');
update "script" content in writePost.vue component
<script setup>
import { ref , getCurrentInstance } from "vue";
const app = getCurrentInstance();
const dispatcher= app?.appContext.config.globalProperties.dispatcher;
const newNote = ref("");
const newWriter = ref("");
const newHeadline = ref("");
const notes = ref([]);
const addNote = () => {
notes.value.push({
id: Math.floor(Math.random() * 1000000),
text: newNote.value,
writer: newWriter.value,
headline: newHeadline.value,
});
// emit notes
dispatcher.emit("updateNotes" , notes);
addLocalStorage(notes)
};
const addLocalStorage = (notes) => {
localStorage.setItem("notes", JSON.stringify(notes))
JSON.parse(localStorage.getItem("notes"));
}
</script>
update "script" content in homePosts.vue component
<script>
export default {
name: 'homePosts',
data() {
return {notes: []}
},
mounted() {
this.notes = JSON.parse(localStorage.getItem("notes") ?? "[]");
this.dispatcher.on("updateNotes" , (notes) => {
this.notes = notes ?? [];
})
},
beforeDestroy() {
this.dispatcher.off("updateNotes");
},
}
</script>
I'm just starting to use VueJS & Tailwind, having never really used anything related to npm before.
I have the below code, making use of Tailwind & Headless UI which through debugging, I know I'm like 99% of the way there... except for the continuous error message
Uncaught ReferenceError: posts is not defined
I know this should be straight forward, but everything I've found either here or with Google hasn't worked. Where am I going wrong?
<template>
<Listbox as="div" v-model="selected">
<ListboxLabel class="">
Country
</ListboxLabel>
<div class="mt-1 relative">
<ListboxButton class="">
<span class="">
<img :src="selected.flag" alt="" class="" />
<span class="">{{ selected.name }}</span>
</span>
<span class="">
<SelectorIcon class="" aria-hidden="true" />
</span>
</ListboxButton>
<transition leave-active-class="" leave-from-class="opacity-100" leave-to-class="opacity-0">
<ListboxOptions class="">
<ListboxOption as="template" v-for="country in posts" :key="country" :value="country" v-slot="{ active, selected }">
<li :class="">
<div class="">
<img :src="country.flag" alt="" class="" />
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
{{ country.latin }}
</span>
</div>
<span v-if="selected" :class="">
<CheckIcon class="" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<script>
import { ref } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
data() {
return {
response: null,
posts: undefined,
};
},
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},
setup() {
const selected = ref(posts[30])
return {
selected,
}
},
}
</script>
The offending line is const selected = ref(posts[30]) which I know I need to somehow define posts, but I don't get how?
CAUSE OF YOUR ERROR:
You are trying to access an array element before the array is populated. Thus the undefined error.
EXPLANATION
You are using a mix of composition api and options api. Stick to one.
I am writing this answer assuming you will pick the composition api.
Follow the comments in the below snippet;
<script>
// IMPORT ONMOUNTED HOOK
import { ref, onMounted } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
// YOU DO NOT NEED TO DEFINE THE DATA PROPERTY WHEN USING COMPOSITION API
/*data() {
return {
response: null,
posts: undefined,
};
},*/
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
// YOU DO NOT NEED THESE LIFE CYCLE HOOKS; COMPOSITION API PROVIDES ITS OWN LIFECYCLE HOOKS
/*mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},*/
setup() {
// YOU ARE TRYING TO ACCESS AN ELEMENT BEFORE THE ARRAY IS POPULATED; THUS THE ERROR
//const selected = ref(posts[30])
const posts = ref(undefined);
const selected = ref(undefined);
onMounted(()=>{
// CALL THE AXIOS METHOD FROM WITHIN THE LIFECYCLE HOOK AND HANDLE THE PROMISE LIKE A BOSS
axios.get('http://localhost')
.then((res) => {
selected.value = res[30];
});
});
return {
selected,
}
},
}
</script>
According to your comment; you should first check if the “selected != null” before using ‘selected’ inside the template. You can use a shorthand version like this
<img :src=“selected?.flag” />
A component that has a hidden input field should have a the value of the current page title. In Nuxt you can accomplish it using asyncData for all pages except for pages using Nuxt Content Module. The Nuxt Content Module does not allow asyncData but fetch is allowed but. Example:
components/Form.vue
<template>
<div>
<input
id="page"
type="hidden"
:value="article.title"
required
/>
</div>
</template>
<script>
export default {
async fetch() {
this.article = await this.$content('articles', this.params).fetch()
},
data() {
return { article: {} }
},
}
</script>
pages/articles/_slug.vue
<template>
<div>
<h1>{{ article.title }}</h1>
<Form />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug).fetch()
return { article }
},
}
</script>
This code gets no errors but the component never shows page title in the hidden input only in the h1 tag. (edit: fixed typo)
How I solved this was with props.
components/Form.vue
<template>
<form>
<label for="title"> Title </label>
<input name="title" type="hidden" :value="title" required />
<button type="submit">Send</button>
</form>
</template>
<script>
export default {
props: {
title: {
type: String,
required: false,
default: ''
},
}
}
</script>
and then in the pages/_slug.vue
<template>
<div>
<nuxt-content :document="doc" />
<Form :title="doc.title" />
</div>
</template>
This should work for you.
I am trying to do a very simple vue example and it won't display. I've done similar things before, but this won't work.
It is an extremely simple task list. It is an input with a submit button that adds an item to a list. For some reason the component does not render at all. I am very lost am supposed to give a presentation on vue. I was hoping to use this as an example.
I'm really not sure what else to say about this, but stack overflow won't let me submit this without typing more information about the issue.
<div id="app">
<task-list></task-list>
</div>
Vue.component('task-list-item', {
props: ["task"],
template: '#task-list-item-template'
})
Vue.component('task-list', {
data: function () {
return {
taskList: [],
newTask: ''
}
},
methods: {
addTask: function () {
var self = this;
if (self.newTask !== ""
&& self.newTask !== null
&& typeof self.newTask !== "undefined") {
this.taskList.push(self.newTask);
this.newTask = "";
}
}
},
template: '#task-list-template'
})
new Vue({
el: '#app',
data: function () {
return {
}
}
})
<script id="task-list-template" type="text/x-template">
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</script>
<script id="task-list-item-template" type="text/x-template">
<li>{{task}}</li>
</script>
I am getting no error messages of any kind.
I think the problem is there should be only 1 child under <script id="task-list-template" type="text/x-template"></script>. In task-list-template, you have multiple children. Try to wrap them in 1 div
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>
Demo on codepen
According to A Single Root Element
Every component must have a single root element
To fix you can do some thing like:
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList" v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>