I have installed vuelidate 2 to validate forms in my NuxtJS project. I followed instructions for installation and setup step by step according to vuelidate documentation. This is how my setup files look until now:
package.json
"dependencies": {
"#nuxtjs/axios": "^5.13.6",
"#vue/composition-api": "^1.2.2",
"#vuelidate/core": "^2.0.0-alpha.26",
"#vuelidate/validators": "^2.0.0-alpha.22",
"cookie-universal-nuxt": "^2.1.5",
"core-js": "^3.15.1",
"nuxt": "^2.15.7",
"uikit": "^3.7.1"
}
plugins/composition-api.js
import Vue from 'vue'
import VueCompositionAPI from '#vue/composition-api'
Vue.use(VueCompositionAPI)
and nuxt.config.js for #vue/composition-api
plugins: [
{ src: '~/plugins/composition-api.js' }
]
and finally this is how I'm using vuelidate inside my component:
<script>
import useVuelidate from '#vuelidate/core'
import { required } from '#vuelidate/validators'
export default {
setup () {
return { v$: useVuelidate() }
},
data () {
return {
contact: {
name: ''
}
}
},
validations () {
return {
contact: {
name: { required }
}
}
},
methods: {
submitForm () {
this.v$.$validate()
.then((isFormValid) => {
if (isFormValid) {
console.log('valid!!!')
} else {
return false
}
})
},
}
}
</script>
<template>
<label>
<input v-model="contact.name">
<div v-if="v$.contact.name.$error">Name is required.</div>
</label>
</template>
These are a couple of problems that occur:
when I place v-if="v$.contact.name.$error" inside template I get the error Cannot read property 'name' of undefined.
When I call submitForm method, the value of isFormValid is always false. Even when I have filled the contact.name field. And validation properties like $dirty don't change.
I have no idea why these happen. What am I doing wrong?
Update: (In case it might be useful to solve the problen) My console errors filter was unchecked by accident and I hadn't seen this Nuxt warning: [vue-composition-api] already installed. Vue.use(VueCompositionAPI) should be called only once.. As I searched about this error I found out Nuxt uses a dependency called Nuxt composition api which depends on #vue/composition-api. But when I reomved #vue/composition-api from plugins even the code inside setup didn't work correctly.
Solution with vuelidate:
create plugin (plugins/vuelidate.js):
import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
nuxt.config:
plugins: [
{ src: '~/plugins/vuelidate' }
],
import:
import { required } from 'vuelidate/lib/validators'
method:
formSubmit() {
this.$v.$touch();
if (!this.$v.$invalid) {
// if invalid datas
}
},
template:
<h3
:class="{
'is-invalid': $v.contact.name.$error,
}"
>
Something
</h3>
Related
I am working on a project in nuxt3 and I wanna add google places auto-complete on search input. I've been search for last 4 hours but can't got a way to set it up.
After long research I found the below solution working for me in the nuxt3 stable version.
Install the following version (0.9.72) of #fawmi/vue-google-maps
your package.json file:
"dependencies": {
"#fawmi/vue-google-maps": "0.9.72",
}
in nuxt.config.ts add following lines
build: {
transpile: ["#fawmi/vue-google-maps"],
},
Then create folder named plugins and make a file inside it named vueGoogleMaps.ts
📦plugins
┗ 📜vueGoogleMaps.ts
Your vueGoogleMaps.ts file:
import { defineNuxtPlugin } from "#app";
import VueGoogleMaps from "#fawmi/vue-google-maps";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGoogleMaps, {
load: {
key: "Your-key",
libraries: "places", // This is required if you use the Autocomplete plugin
},
autobindAllEvents: true,
});
});
then in example.vue:
<template>
<GMapAutocomplete
placeholder="This is a placeholder"
#place_changed="setPlace"
>
</GMapAutocomplete>
</template>
<script>
export default {
name: 'App',
data() {
return {
}
},
methods: {
setPlace() {
}
}
}
</script>
I use vue3 with composition api, but when I build my project, the ref element always undefined.
I reproduced it, maybe I used it incorrectly, but I don't know why.
I defined a ref in hooks function.
const isShow = ref(false)
const rootRef = ref<HTMLDivElement>();
export default function () {
function changeShow() {
isShow.value = !isShow.value;
console.log(isShow.value, rootRef.value);
}
return { isShow, rootRef, changeShow };
}
Use rootRef in the HelloWorld.vue and linked element.
<script setup lang="ts">
import useShow from "../composables/useShow";
const { rootRef, isShow } = useShow();
</script>
<template>
<div ref="rootRef" v-show="isShow" class="test"></div>
</template>
Create a button in App.vue and bind click function.
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
import useShow from "./composables/useShow";
const { changeShow } = useShow();
</script>
<template>
<button #click="changeShow">切换</button>
<HelloWorld />
</template>
When I click button, it works.
But when I build it and import from lib, it doesn't work.
My vite.config.ts is as follows:
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"#": path.resolve(__dirname, "src")
}
},
build: {
cssCodeSplit: true,
sourcemap: true,
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "my-project",
fileName: format => `my-project.${format}.js`
},
rollupOptions: {
external: ["vue"],
preserveEntrySignatures: "strict",
output: {
globals: {
vue: "Vue"
}
}
}
}
});
I think the problem is the definition of rootRef. It seems that only binding location can use it. This is no different from defining it in a component. I need to use it in multiple places.
Oddly, in this way, the Dev environment works fine, but Pro env is not available. Do I need to modify the build configuration of vite.
How do I do that?
The problem is your App.vue uses its own copy of composables/useShow instead of the one from the lib.
The solution is to export the composable from the lib so that your app can use the same one:
// src/index.ts
import { default as useShow } from './composables/useShow';
//...
export default {
//...
useShow
};
In App.vue, use the lib's composable:
import MyProject from "../dist/my-project.es";
const { changeShow } = MyProject.useShow();
GitHub PR
I'm new to vue.js. Integrated CKEditor successfully but having trouble to integrate CKFinder in it. I'm trying to Import CKFinder in CKEditor component but I'm getting an error.
CKEditor-Vue Component:
<template>
<ckeditor :editor="editor" :value="defaultValue"
#input="editorInput" :disabled="disabled" :config="editorConfig"></ckeditor>
</template>
<script>
import DecoupledEditor from '#ckeditor/ckeditor5-build-decoupled-document';
import CKFinder from '#ckeditor/ckeditor5-ckfinder/src/ckfinder'
export default {
name: "Editor",
props: {
defaultValue: String,
disabled: Boolean
},
data() {
return {
editor: DecoupledEditor,
editorConfig: {
plugins: [
CKFinder
]
}
}
},
methods: {
editorInput(e) {
this.$emit('getEditorData', e);
}
}
}
</script>
<style scoped>
</style>
When I try to import CKFinder it's showing ckeditor-duplicated-modules: Some CKEditor 5 modules are duplicated.. Screenshot:
Am I doing anything wrong? Do you have any integration guide or correction on my component?
Thanks in advance for helping me.
You don't have to import
import CKFinder from '#ckeditor/ckeditor5-ckfinder/src/ckfinder'
because it is already included in the build of your choice. You only need to configure it and the error should disappear.
To make it clear like what oleq mean, just make a config under the editorConfig like this. You don't need to import it.
<script>
import DecoupledEditor from '#ckeditor/ckeditor5-build-decoupled-document';
import CKFinder from '#ckeditor/ckeditor5-ckfinder/src/ckfinder'
export default {
name: "Editor",
props: {
defaultValue: String,
disabled: Boolean
},
data() {
return {
editor: DecoupledEditor,
editorConfig: {
plugins: [
ckfinder: {
uploadUrl:
'/ckfinder/connector?command=QuickUpload&type=Files&responseType=json',
filebrowserBrowseUrl: '/ckfinder/browser',
filebrowserImageBrowseUrl: '/ckfinder/browser?type=Images',
filebrowserUploadUrl:'/ckfinder/connector?command=QuickUpload&type=Files',
filebrowserImageUploadUrl: '/ckfinder/connector?command=QuickUpload&type=Images'
}
]
}
}
},
methods: {
editorInput(e) {
this.$emit('getEditorData', e);
}
}
}
</script>
I'm writing a couple of examples for work, and one that's hanging me up is injecting a service that's provided during Vue's bootstrapping.
This "works" (I can access it, it compiles, and runs), and there are no problems or complaints with my JavaScript version nor the Class-Component TypeScript version, but the compiler complains that this.sampleService doesn't exist in my component when using the Vue object API with TypeScript.
Am I missing something?
<template>
<div class="app">
{{message}} <fake-button :text="buttonText" #some-click-event="handleClickEvent"></fake-button>
</div>
</template>
<style lang="scss">
.app {
$background-color: #9f9;
$foreground-color: #000;
background: $background-color;
color: $foreground-color;
}
</style>
<script lang="ts">
import Vue from 'vue'
const App = Vue.extend({
components: {
FakeButton: () => import('#client/components/fake-button/fake-button-object-typescript.vue')
},
data: function () {
return {
message: 'Hello World - App Object TypeScript',
buttonText: 'Click Me'
}
},
inject: {
sampleService: 'sampleService'
},
methods: {
handleClickEvent(someVal?: string) {
console.log('App', 'handleClickEvent', someVal);
}
},
beforeCreate() {
console.log('App', 'beforeCreate');
},
created() {
console.log('App', 'created');
},
mounted() {
console.log('App', 'mounted');
// TODO: While this compiles, TypeScript complains that it doesn't exist
console.log('this.sampleService.getDate()', this.sampleService.getDate());
}
});
export default App;
</script>
ERROR in vue-test/src/client/containers/app/app-object-typescript.vue.ts
[tsl] ERROR in vue-test/src/client/containers/app/app-object-typescript.vue.ts(35,56)
TS2339: Property 'sampleService' does not exist on type 'CombinedVueInstance<Vue, { message: string; buttonText: string; }, { handleClickEvent(someVal?: s...'.
My solution for this problem was to create a interface for the injection. In you example that would be something like the follwing:
<script lang="ts">
import { SampleServiceInjection } from '...';
const App = ( Vue as VueConstructor<Vue & SampleServiceInjection> ).extend({
inject: {
sampleService: 'sampleService'
} as Record<keyof SampleServiceInjection, string>,
// etc.
});
</script>
You can use that very interface in component that provides the service as well:
export interface SampleServiceInjection {
sampleService: SampleService; // or whatever type your service has
}
export default Vue.extend({
provide(): SampleServiceInjection {
return {
sampleService: new SampleService(),
};
},
// etc.
});
Try adding provide. Check example below. I would suggest using vue-property-decorator since your are leveraging typescript.
inject: {
sampleService: 'sampleService'
},
provide () {
return {
sampleService: this.sampleService,
}
}
My package.json:
"dependencies": {
"bootstrap": "^3.3.7",
"mathjax": "^2.7.2",
"vue": "^2.5.2",
"vue-moment": "^3.1.0",
"vue-router": "^3.0.1"
},
I have a component:
<template>
<div class="post--body" v-html="previewText" id="post--body"></div>
</template>
<script>
import MathJax from 'mathjax'
export default {
name: 'blog-post',
data () {
return {
post: {body: ""}
}
},
mounted() {
fetch("/api/post/" + this.$route.params.id)
.then(response => response.json())
.then(data => {
this.post = data;
})
this.$nextTick(function () {
console.log("tick")
MathJax.Hub.Typeset()
})
},
computed: {
previewText () {
return this.post.body
}
}
}
</script>
But I got "Uncaught SyntaxError: Unexpected token <" on MathMenu.js?V=2.7.2:1
How to properly use mathjax?
I don't think you can import mathjax, because if I console log imported mathjax , it shows empty object. I have gone through the folder directory also that doesn't seem importable. So you need to manually put the script src pointing to Mathjax.js
The way I currently use Mathjax in vue is by making a custom global component.
<template>
<span ref="mathJaxEl" v-html="data" class="e-mathjax"></span>
</template>
<script type="text/javascript">
export default{
props:['data'],
watch:{
'window.MathJax'(val){
this.renderMathJax()
},
'data'(val){
this.renderMathJax()
}
},
mounted(){
this.renderMathJax()
},
methods:{
renderMathJax(){
if(window.MathJax){
window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub,this.$refs.mathJaxEl]);
}
}
}
}
</script>
It can be made a bit better by using a variable to save boolean whether, mathjax has been rendered or not, as rendering gets called for two watch values, which both may get triggered in case of browser refresh.
So for MathJax v3, just add the following to your vue component
mounted(){
MathJax.typeset();
},
Now when navigating to pages via Vue router the math will render on component mount.