Vue Composition API createComponent child components - vue.js

I'm simply trying to use a child component in a Nuxt/Composition API/TypeScript project using Vuetify. I've tried including the component before the setup method, but when I view the source I only see <!----> where the child component data should be.
This is the layouts/default.vue layout that should be displaying the "nested" component.
<template>
<v-app>
<v-app-bar clipped-left app color="indigo darken-2" />
<v-content>
<nuxt />
</v-content>
<v-bottom-navigation
v-model="bottomNav"
color="indigo darken-2"
absolute
class="hidden-sm-and-up"
>
<v-btn
value="search"
#click="dialog=true"
>
<span>Search</span>
<v-icon>mdi-magnify</v-icon>
</v-btn>
</v-bottom-navigation>
<v-dialog v-model="dialog">
<ListFilter
#closeDialog="closeDialog"
/>
</v-dialog>
</v-app>
</template>
<script lang="ts">
import { createComponent, ref } from '#vue/composition-api'
import ListFilter from '~/components/ListFilter.vue'
export default createComponent({
components: { ListFilter },
setup() {
const bottomNav = ref<string>()
const dialog = ref<boolean>(false)
const closeDialog = () => {
dialog.value = false
bottomNav.value = ''
}
return {
bottomNav,
dialog,
closeDialog
}
}
})
</script>
Here is the sample contents of the components/ListFilter.vue component to be nested, the contents of which matter little.
<template>
<v-card>
<v-card-title>
Search
</v-card-title>
<v-card-text>
Test Text
</v-card-text>
<v-card-actions>
<v-btn
#click="closeDialog"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</template>
<script lang="ts">
import { createComponent } from '#vue/composition-api'
export default createComponent({
setup(_, { emit }) {
const closeDialog = () => {
emit('closeDialog')
}
return closeDialog
}
})
</script>

Problem is return closeDialog in setup function of ListFilter component. If you return a function from setup(), Vue expects it is a render function. Try return { closeDialog } instead...

Related

Pass click function with input v-model params

I have a btnSmall component with this props and click
<v-btn
class="white--text btn_main_product"
color="red darken-1"
#click="click"
>
<slot/>
</v-btn>
props: {
click: Function
}
I use this component like this in another component.
<template>
<v-card
class="pa-4"
>
<v-text-field
v-model="lname"
></v-text-field>
<v-btn :click="clickAccept(lname)">accept/v-btn>
</v-card>
</template>
<script>
import BtnSmall from "../buttons/btnSmall";
export default {
name: "editName",
components: {BtnSmall},
data() {
return {
lname: ''
}
},
props: ['clickAccept']
}
</script>
and use this component in the page.
after use <edit-name :click-accept="nameClickAccept"/> whit this method.
nameClickAccept(fname, lname) {
console.log(lname)
}
my method run with change text-field i cant use click.
i need run method with click on the btn.
The vue-approach would be to emit an event in the btnSmall component and react on that event in the parent component.. This: https://v3.vuejs.org/guide/component-custom-events.html#event-names shows you how you can do it.

vue access valid data from parent component

I can access method from parent vue component using ref and now I would like to access valid data from parent vue component using ref too
Parent.vue:
<v-btn #click="savenewcase()" dark text :disabled="!valid">Save</v-btn></v-toolbar-items>
<NewCaseDialog ref="NewCase"></NewCaseDialog>
<script>
methods: {
savenewcase() {
this.$refs.NewCase.save()
}
}
</script>
NewCaseDialog.vue
<template>
<v-card>
<v-form v-model="valid" ref="NewCaseForm" #keyup.native.enter="save()">
<v-container>
<v-text-field
:counter="64"
v-model="vsubject"
label="Subject / Judul"
prepend-icon="subject"
></v-text-field>
</v-container>
</v-form>
<v-btn #click="save()" :disabled="!valid" color="primary">Save</v-btn>
</v-card>
</template>
<script>
data: () => ({
valid: false,
}),
methods:{
save() {
//run save
}
}
</script>
Edit :
I'm using vuetify
You can try Props instead, it's much easier. From docs:
child-component.vue
Vue.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
parent.vue
<blog-post post-title="hello!"></blog-post>

Vue: Data is not changing with Vuex getter?

I have a modal component that I was triggered when different components mutate the triggerModalState field. I have a getter in my Vuex store called getFulfillmentModalState. The modal is driven off of the local data field called dialog, which I have set to be the value of the getter this.$store.getters.getFulfillmentModalState. I am checking the value of this.$store.getters.getFulfillmentModalState, and it is true after I trigger the modal, but the dialog data is still false. What am I doing wrong here?
<template>
<div class="fulfillment-modal-component">
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="290">
<v-card>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="rgba(53, 87, 151, 0.85)" text #click="changeModalState(true)">Cancel</v-btn>
<v-btn color="rgba(53, 87, 151, 0.85)" text #click="changeModalState(false)">Continue</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</div>
</template>
<script>
import {mapState} from 'vuex';
import store from './../../store/store';
export default {
name: 'fulfillment-modal',
mounted() {
},
data() {
return {
dialog: this.$store.getters.getFulfillmentModalState
}
},
}
</script>
dialog isn't reactive in this case because it's declared in data(). Move dialog to computed to make it reactive:
//...
export default {
data() {
return {
//dialog: /*..*/ // move to computed
}
},
computed: {
dialog() {
return this.$store.getters.getFulfillmentModalState
}
}
}
demo

Vuetify error message color is not showing

I currently have a login form that is using vuetify and vuelidate. Vuetify is used to create the form and display error messages, whereas vuelidate is used to validate user input. My problem is that the error messages are not displayed in red. Below are my codes and a screenshot of the login form
Main.js
import Vue from 'vue'
import App from './App.vue'
import Vuelidate from 'vuelidate'
import router from './router'
import store from './store'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
Vue.use(Vuetify)
Vue.use(Vuelidate)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Login.vue
<template>
<!-- v-container is used to center the contents -->
<v-container>
<v-form>
<v-flex xs12 sm10 md8 lg6>
<v-text-field
label="Full Name"
v-model.trim="fullName"
#input="$v.fullName.$touch()"
:counter="10"
:error-messages="fullNameErrors"
></v-text-field>
</v-flex>
<v-flex xs12 sm10 md8 lg6>
<v-text-field
label="Email"
v-model.trim="email"
#input="$v.email.$touch()"
:error-messages="emailErrors"
></v-text-field>
</v-flex>
<v-flex xs12 sm10 md8 lg6>
<v-text-field
label="Password"
v-model.trim="password"
#input="$v.password.$touch()"
:error-messages="passwordErrors"
></v-text-field>
</v-flex>
<v-btn #click="submit">submit</v-btn>
<v-btn #click="clear">clear</v-btn>
</v-form>
</v-container>
</template>
<script>
import { required, email, maxLength } from "vuelidate/lib/validators";
export default {
data() {
return {
fullName: "",
email: "",
password: ""
};
},
validations: {
email: {
required,
email
},
password: {
required
},
fullName: {
required,
maxLength: maxLength(10)
}
},
computed: {
fullNameErrors() {
const errors = [];
if (!this.$v.fullName.$dirty) return errors;
!this.$v.fullName.maxLength &&
errors.push("Name must be at most 10 characters long");
!this.$v.fullName.required && errors.push("Name is required.");
return errors;
},
emailErrors() {
const errors = [];
if (!this.$v.email.$dirty) return errors;
!this.$v.email.email && errors.push("Must be valid e-mail");
!this.$v.email.required && errors.push("E-mail is required");
return errors;
},
passwordErrors() {
const errors = [];
if (!this.$v.password.$dirty) return errors;
!this.$v.password.required && errors.push("Password is required");
return errors;
}
},
methods: {
submit() {
this.$v.$touch();
},
clear() {
this.$v.$reset();
this.fullName = "";
this.email = "";
this.password = "";
}
}
};
</script>
Looks like its missing some CSS and font.
I am using the stylus import with no issues.
npm install roboto-fontface
npm install vuetify
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/src/stylus/main.styl'
import 'roboto-fontface/css/roboto/roboto-fontface.css'
Vue.use(Vuetify);
Stylus requires extra npm plugins:
stylus
stylus-loader
vue-style-loader
Further debugging, ensure all of your projects components are
encapsulated in a layout with the v-app tag, standard vue project with an App.vue file expects v-app tag inside its template.
App.vue:
<template>
<v-app>
<login />
</v-app>
</template>
v-app produces the following wrapper div tags that are critical to styles being applied to content within:
<div data-app="true" class="application theme--light" id="app">
<div class="application--wrap">
code injects here
</div>
</div>

Vuetify: Navigation drawer: communicate v-model changes to parent co

I have created a Sidebar component using Vuetify's navigation drawer. The code looks something like this:
<template>
<v-navigation-drawer persistent clipped v-model="isVisible" fixed app>
<!-- content of the sidebar goes here -->
</v-navigation-drawer>
</template>
<script>
export default {
name: 'Sidebar',
props: {
visible: Boolean,
},
data() {
return {
isVisible: this.visible,
};
},
}
</script>
Please note that I am duplicating the visible prop with the isVisible data. I tried using the prop directly in the v-model but every time the sidebar closed, I would get a warning in the console about changing props directly, as they would be overwritten when the parent re-renders.
In the parent view, I have a button on the toolbar that is supposed to change icon depending on the visibility of the toolbar.
<template>
<v-container fluid>
<sidebar :visible="sidebarVisible"/>
<v-toolbar app :clipped-left="true">
<v-btn icon #click.stop="sidebarVisible = !sidebarVisible">
<v-icon v-html="sidebarVisible ? 'chevron_right' : 'chevron_left'"/>
</v-btn>
</v-toolbar>
<v-content>
<router-view/>
</v-content>
<v-footer :fixed="fixed" app>
<span>© 2017</span>
</v-footer>
</v-container>
</template>
<script>
import Sidebar from '#/components/Sidebar.vue';
export default {
name: 'MainView',
data() {
return {
sidebarVisible: false,
fixed: false,
title: 'Title',
};
},
components: {
Sidebar,
},
};
</script>
The problem I have is that if I close the sidebar by clicking outside of it, the icon of the button on the toolbar does not change to chevron-left. Moreover, in order to bring the sidebar back, I need to click on the button twice.
Clearly this is because the sidebarVisible data in the main view is not updated when the sidebar closes. How do I make sure that sidebarVisible is updated when the sidebar closes?
I am use next construction...
in my component
<template>
<v-navigation-drawer v-model="localDrawer"></v-navigation-drawer>
</template>
...
<script>
export default {
props: { value: { type: Boolean } },
data: () => ({
localDrawer: this.value
}),
watch: {
value: function() {
this.localDrawer = this.value
},
localDrawer: function() {
this.$emit('input', this.localDrawer)
}
}
}
</script>
in parent layer
<app-drawer v-model="drawer"></app-drawer>
it's work for me
Use v-bind:value or :value to bind the drawer value from props.
Child component:
<template>
<v-navigation-drawer v-bind:value="drawer"></v-navigation-drawer>
</template>
<script>
export default {
props : ['drawer']
}
</script>
Parent component:
<template>
<app-side-bar :drawer="drawer"/>
<v-app-bar app clipped-left>
<v-app-bar-nav-icon #click.stop="drawer = !drawer"></v-app-bar-nav-icon>
</v-app-bar>
</template>
Vuetify navigation drawer issue fix:
Reset your browser window to default 100%
Here is the code,
Template:
<nav>
<v-toolbar flat app>
<v-toolbar-side-icon class="grey--text" #click="toggle"></v-toolbar-side-icon>
<v-toolbar-title class="text-uppercase grey--text">
<span class="font-weight-light">Repo</span>
<span>hub</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn flat color="grey">
<span>Sign Out</span>
<v-icon right>exit_to_app</v-icon>
</v-btn>
</v-toolbar>
<v-navigation-drawer app v-model="drawer" class="indigo">
<p>test</p>
</v-navigation-drawer>
</nav>
Script:
export default {
data() {
return {
drawer: false
};
},
methods:{
toggle(){
this.drawer = !this.drawer;
}
}
};