Vue component does not render and ignores props - laravel-5

I am having a problem with a Vue component which should just show a simple dialog, the component looks like this:
<template>
<v-layout row justify-center>
<v-dialog
v-model="show"
max-width="290"
:persistent="persistent"
>
<v-card>
<v-card-title class="headline grey lighten-2">{{header}}</v-card-title>
<v-card-text v-html="text">
{{text}}
</v-card-text>
<v-card-actions>
<v-layout>
<v-flex xs6 md6 lg6 class="text-xs-center">
<v-btn block
color="primary"
flat
#click="closeDialog(true)"
>
{{agree_button_text}}
</v-btn>
</v-flex>
<v-flex xs6 md6 lg6 class="text-xs-center">
<v-btn block
color="warning"
flat
#click="closeDialog(false)"
>
{{abort_button_text}}
</v-btn>
</v-flex>
</v-layout>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</template>
<script>
export default {
props:
{
persistent:
{
type: Boolean,
required: false,
default: false
},
header:
{
type: String,
required: false,
default: ""
},
text:
{
type:String,
required: false,
default:""
},
abort_button_text:
{
type: String,
required: false,
default:"Avbryt"
},
agree_button_text:
{
type: String,
required: false,
default: "OK"
},
value:
{
}
},
data ()
{
return {
show: this.value
}
},
methods:
{
closeDialog:
function(retval)
{
this.show = false;
this.$emit('close-dialog-event',retval);
}
}
}
</script>
In app.js I have added the following:
require('./bootstrap');
import babelPolyfill from 'babel-polyfill';
import Vuetify from 'vuetify'
window.Vue = require('vue');
var vueResource = require('vue-resource');
window.socketIo = require('vue-socket.io');
Vue.use(vueResource);
Vue.use(Vuetify);
Vue.http.headers.common['X-CSRF-TOKEN'] = $('meta[name="csrf-token"]').attr('content');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('simple-dialog', require('./components/common/SimpleDialog.vue'));
And on the page where I use the component I have:
<div id="overview">
<simple-dialog show="true"
:header="dialog_header"
:text="dialog_message"
#close-dialog-event="display_dialog = false"></simple-dialog>
<v-app>
<v-content>
<v-container>
FOO AND BAR
</v-container>
</v-content>
</v-app>
</div>
I don't get any errors that the component is not loaded.
And if I try to change the name of the component in the app.js file and then try to load the component it throws an error that the component can not be found. So in others words it seems that it loads successfully. However, if I change the name of the props, e.g. changing
<simple-dialog show="true"
:header="dialog_header"
:text="dialog_message"
#close-dialog-event="display_dialog = false"></simple-dialog>
to
<simple-dialog show_bla="true"
:asdasd="dialog_header"
:asdasd="dialog_message"
#close-dialog-event="display_dialog = false"></simple-dialog>
it does not care. It does not even throw an error about that those props either does not exists or are invalid. The javascript used by the page:
var app = new Vue({
el: '#overview',
data:
{
display_dialog:true,
dialog_header:'',
dialog_message:'',
},
methods:
{
}
});
What could the problem be? Thanks for any help!

Well, When you're sending the value to the component and your prop show is initialized as an empty object.
replace
value: {}
to
value
or
the pass default value to false
value: {
type: Boolean
default: false
}
Hope this helps!

Related

what am I doing wrong by passing a prop from the parent to the child in vue if it only works in the template side?

So i am trying to do a basic WebSocket notification system. I am passing the user as a prop from App.vue to the navbar component. it Can be shown in the template but when I try to call it in the script section. it says undefined.You can take a look at this picture, it shows the id shown in the navbar and when I try to console.log it, it says undefined.
Here is my App.vue "The parent"
<template lang="">
<div>
<v-app>
<template v-if="isLoggedIn">
<AdminMenu></AdminMenu>
</template>
<template v-else-if="!isUserLoggedIn">
<Nav></Nav>
</template>
<template v-if="isUserLoggedIn">
<Navbar :user='user.id'></Navbar>
</template>
<v-main app>
<router-view></router-view>
</v-main>
</v-app>
</div>
</template>
<script>
import 'vuetify/dist/vuetify.min.css' // Ensure you are using css-loader
import axios from 'axios';
import AdminMenu from './layouts/AdminMenu.vue'
import Navbar from './layouts/user/Navbar.vue'
import Nav from './layouts/user/Nav.vue'
export default {
name:'app',
components:{ 'AdminMenu': AdminMenu, 'Navbar':Navbar, 'Nav':Nav},
data(){
return{
user:[],
isLoggedIn: false,
isUserLoggedIn: false,
}
},
created() {
if (window.Laravel.isLoggedin) {
this.isLoggedIn = true
}
if (window.Laravel.isUserLoggedin) {
this.isUserLoggedIn = true
}
},
mounted(){
this.axios.get('/api/user').then(response=>{
this.user = response.data
}).catch(error=>{
console.log(error)
})
},
}
</script>
Here is the child
<template lang="">
<div>
<v-toolbar app dark>
<span class="hidden-sm-and-up">
<v-toolbar-side-icon #click="sidebar = !sidebar">
</v-toolbar-side-icon>
</span>
<v-toolbar-title>
<router-link to="/" tag="span" style="cursor: pointer">
{{ appTitle }}
</router-link>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items class="hidden-xs-only">
<v-btn
text
v-for="item in menuItems"
:key="item.title"
:to="item.path">
{{ item.title }}
</v-btn>
<v-btn flat icon color="primary" disabled>
<v-icon></v-icon>{{user}}
</v-btn>
<v-btn #click="logout">Logout</v-btn>
</v-toolbar-items>
</v-toolbar>
</div>
</template>
<script>
export default {
name: "nav",
props:['user'],
data(){
return {
appTitle: 'My template',
sidebar: false,
menuItems: [
{ title: 'Home', path: '/home', icon: 'home' },
{ title: 'Profile', path: '/profile', icon: 'face' },
]
}
},
created(){
console.log(this.user)
},
methods: {
logout(e) {
console.log('ss')
e.preventDefault()
this.axios.get('/sanctum/csrf-cookie').then(response => {
this.axios.post('/api/logout')
.then(response => {
if (response.data.success) {
window.location.href = "/"
} else {
console.log(response)
}
})
.catch(function (error) {
console.error(error);
});
})
},
},
};
</script>
<style lang="">
</style>
First of all define your user in data() with default values so you should not recieve undefined error Moreover there is no async/await when you are calling api in the mounted state
App.vue
<template>
<div>
<v-app>
<template v-if="isLoggedIn">
<AdminMenu></AdminMenu>
</template>
<template v-else-if="!isUserLoggedIn">
<Nav></Nav>
</template>
<template v-if="isUserLoggedIn">
<Navbar :user='user.id'></Navbar>
</template>
<v-main app>
<router-view></router-view>
</v-main>
</v-app>
</div>
</template>
<script>
import 'vuetify/dist/vuetify.min.css' // Ensure you are using css-loader
import axios from 'axios';
import AdminMenu from './layouts/AdminMenu.vue'
import Navbar from './layouts/user/Navbar.vue'
import Nav from './layouts/user/Nav.vue'
export default {
name:'app',
components:{ 'AdminMenu': AdminMenu, 'Navbar':Navbar, 'Nav':Nav},
data(){
return{
user:[],
isLoggedIn: false,
isUserLoggedIn: false,
}
},
created() {
if (window.Laravel.isLoggedin) {
this.isLoggedIn = true
}
if (window.Laravel.isUserLoggedin) {
this.isUserLoggedIn = true
}
},
async mounted(){
await this.axios.get('/api/user').then(response=>{
this.user = response.data
}).catch(error=>{
console.log(error)
})
},
}
</script>

No http lib found to perform ajax request in VJSF

I use VJSF to fill selects using the results from HTTP requests.
Then I faced a bug that is stressful.
Following is my code.
<template>
<v-app id="app">
<v-container>
<p>valid={{valid}}</p>
<v-form ref="form" v-model="valid">
<v-jsf v-model="model" :schema="schema" :options="options" #input="logEvent('input', $event)" #change="logEvent('change', $event)" />
</v-form>
<v-layout row class="mt-2">
<v-spacer></v-spacer>
<v-btn color="primary" #click="$refs.form.validate()">Validate</v-btn>
</v-layout>
</v-container>
</v-app>
</template>
<script>
import VJsf from '#koumoul/vjsf/lib/VJsf.js'
import '#koumoul/vjsf/lib/VJsf.css'
import '#koumoul/vjsf/lib/deps/third-party.js'
const model = {
selectAjaxString: 'https://koumoul.com/s/data-fair/api/v1/datasets/gendarmeries-france-metropolitaine'
}
const options = {
context: {
owner: {
type: 'organization',
id: '5a5dc47163ebd4a6f438589b'
}
},
idPrefix: 'example-select-http-'
}
const schema = {
type: 'object',
properties: {
selectAjaxString: {
type: 'string',
title: 'I\'m a string selected from results of an HTTP request',
'x-fromUrl': 'https://koumoul.com/s/data-fair/api/v1/datasets?status=finalized&select=title&owner={context.owner.type}:{context.owner.id}',
'x-itemsProp': 'results',
'x-itemTitle': 'title',
'x-itemKey': 'href'
}
}
}
export default {
name: 'App',
components: { VJsf },
data: () => ({
model,
options,
schema,
valid: false
}),
methods: {
logEvent(key, $event) {
console.log("vjsf event", key, $event);
}
}
};
</script>
What is the reason?
Why I can not perform ajax request?
I followed sample in https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/examples#select-http
It looks like VSJF requires users to specify an HTTP request library via the httpLib configuration option:
Install axios:
npm i -S axios
Update your VSJF configuration options to set axios as the httpLib:
import axios from 'axios'
const options = {
//...
httpLib: axios
}

Vuetify.js: v-dialog is not working when i close and open again

I using a Vuetify.js for my project
when I used v-dialog component I got a problem that closing the v-dialog and open again.
here is my code.
<div #click="dialog=true">click here</div>
<v-dialog v-model="dialog">
<alert-popup />
</v-dialog>
data() {
return {
dialog : false
}
this is work when I open dialog first time but when I open again only can see the opacity black page
I don`t know which part is wrong. please reply this question. thanks
You didn't provide any code to reproduce the behavior. But I guess the you need provide <v-card> component inside the dialog component. If you don't provide that it just shows the opacity issue.Putting a v-card will eliminate the opacity issue.
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<v-dialog v-model="dialog" max-width="300px">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="dialog = false">Close</v-btn>
<v-btn x-small color="blue darken-1" text #click="dialog = false">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</v-container>
</v-app>
</div>
Here is the working code where there is a v-card component inside v-dialog.
I also see that you've used a custom component called alert-popup inside the v-dialog.If that is the case it needs to be refactored a bit to achieve what you're looking for.
Creating a separate component which you are already doing except <v-dialog>.
Emitting a close event from <alert-popup> component so that open/close and clicking open won't cause issue.
HTML:
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<alert-popup :dialog="dialog" #close="closeMyDialog" />
</v-row>
</v-container>
</v-app>
</div>
Javascript:
let AlertPopup = Vue.component("AlertPopup", {
props: {
dialog: {
type: Boolean,
default: false
}
},
data: () => ({
open: false
}),
methods: {
close() {
this.open = false;
this.$emit("close");
}
},
watch: {
dialog(value) {
this.open = value;
}
},
created() {
this.open = this.dialog;
},
template: `
<v-dialog v-model="open" max-width="300px">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="close">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
`
});
new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
AlertPopup
},
data: (vm) => ({
dialog: false
}),
computed: {},
methods: {
closeMyDialog() {
this.dialog = false;
},
openDialog() {
this.dialog = true;
}
}
});
If look at the AlertPopup component, it just take a prop named dialog and that we can pass from the main component, when button is clicked, it sets to true, which triggers the AlertPopup component.
NOTE:
If we don't emit the close event from the AlertPopup component, then the modal dialog will not open on the second time onwards. The reason for this behavior is the fact that, when the button is clicked from the parent component,which set the dialog to true and pass it to the AlertPopup and whatever things happens inside the AlertPopup remains inside that component and dialog property of parent component never changes.
Here is a working example of how the modal component are triggered from parent component. If we remove the close event, it will not open the modal second time.
Update: It turns out we are unnecessary complicating things, we don't
even need to emit an event, we can pass in the close function as a
prop and react to that, whenever the close button is clicked, it would
call the closeAlert().
Thanks to #Pratik149 for pointing out the bug, I have attached the click outside event handler.
Here is the completely re-factored code.
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<alert-popup :dialog="dialog" #close="closeMyDialog" :close="closeMyDialog" />
</v-row>
</v-container>
</v-app>
</div>
And the component logic is updated as follow
let AlertPopup = Vue.component("AlertPopup", {
props: {
dialog: {
type: Boolean,
default: false
},
close: {
type: Function,
default: () => {}
}
},
data: () => ({
open: false
}),
methods: {
closeAlert() {
this.close();
}
},
template: `
<v-dialog v-model="dialog" max-width="300px" #click:outside="closeAlert">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="closeAlert">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
`
});
new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
AlertPopup
},
data: (vm) => ({
dialog: false
}),
computed: {},
methods: {
closeMyDialog() {
debugger;
this.dialog = false;
},
openDialog() {
this.dialog = true;
}
}
});
Finally here is the updated codepen

Laravel/vuejs Change boolean value from 0 to 1

I have a question I need to create a 2 factor authentication only now the value of a colum in the database will be set to true. He is default to false. I know the database adds this as tinyint so the value should be switched to 1.
So i have try something but didnt work.. im very new with laravel and vuejs. so its hard for me. I hope one of you can help me out of this struggle
My 2 factor vue. So you can see the v-switch thats the button..
<template>
<v-container class="user-form-lime" fluid grid-list-xl>
<v-form>
<v-layout row wrap>
<v-flex xs12 md6>
<v-switch v-model="tfaEnabled"
label="Tweefactor authenticatie"
name="tfaEnabled"
prepend-icon="lock"
#change="change" />
</v-flex>
<v-flex xs12 md6>
<v-text-field v-if="tfaEnabled"
v-model="google2fa.token"
label="Token"
:rules="[rules.required]"
type="text" />
</v-flex>
</v-layout>
<v-layout row wrap>
<v-flex xs12>
<v-btn color="success" #click="submit">
Opslaan
</v-btn>
</v-flex>
</v-layout>
</v-form>
</v-container>
</template>
<script>
export default {
name: 'UserForm2fa',
props: {
id: { type: Number, required: true }
},
data() {
return {
tfaEnabled: false,
google2fa: {
token: '',
},
rules: {
required: val => !!val || 'Dit veld mag niet leeg zijn',
}
};
},
methods: {
changeStatus() {
this.$emit( 'change', this.tfaEnabled );
},
submit() {
this.$emit('submit', this.token)
}
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
My controller function:
public function update2faStatus( Update2faStatus $request ) {
$user = User::findOrFail( $request->id );
$tfaEnabled = $request->input('tfaEnabled', false);
$user->tfaEnabled = $tfaEnabled;
$user->save();
}
and my Method for this:
toggle2fa( status ) {
this.$store.dispatch( 'update2faStatus' )
.then( () => this.$store.dispatch('addMessage', { success: true, content: ['2 Factor authenticatie is ingeschakeld.'] } ) )
.catch( error => this.$store.dispatch( 'addMessage', { success: false, content: error.response.data} ))
},
Its because you have not defined any method with name change(). #change event expects a method named change() in your code. But I guess you are trying to execute changeStatus() method when toggle button is changed. Changing your code to #change="changeStatus()" should fix the problem.

Vuetify Snackbar leave event

I manage to implement a global Vuetify Snackbar.
My problem is to detect when the snackbar close. I read that this component support Vue transition event since 1.2. But it work only on the enter event not the leave ones.
here a fiddle for comprehension.
<transition #before-enter="beforeEnter" #before-leave="beforeLeave" #after-enter="afterEnter" #after-leave="afterLeave" #leave="leave">
<v-snackbar v-model="snackbar" top right>
Hello
<v-btn #click="snackbar = false" dark>Close</v-btn>
</v-snackbar>
</transition>
I faced the same problem and solved this way:
export default {
data: () => ({
errorMessage: '',
snackTimeout: 6000,
}),
watch: {
errorMessage() {
setTimeout(() => {
this.clearErrorMessage();
}, this.snackTimeout);
},
},
methods: {
setErrorMessage(message) {
this.snackMessage = message;
},
clearErrorMessage() {
this.snackMessage = '';
},
},
};
<template>
<v-snackbar
:value="errorMessage"
:timeout="snackTimeout"
top
>
{{ errorMessage }}
<v-btn
color="error"
flat
#click.stop="clearErrorMessage"
>
{{ 'close' }}
</v-btn>
</v-snackbar>
</template>
Define an attribute with the timeout and another with the message to show by the snackBar.
Define a function to set the message and another to clear it.
Define a watch for the message text and set a timer with the same timeout of the snackBar to clear it.
The snackBar appears only when the message is not empty.
You can use get and set methods to handle reading and updating the bound model separately.
I made a generic snackbar component that can be triggered from any other component. I'm using Vuex, vue-property-decorator and typescript here, so adjust accordingly.
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<template>
<v-snackbar v-model="snackbar" max-width="100%">
<template v-slot:action="{ attrs }">
{{ text }}
<v-btn color="primary" text fab v-bind="attrs">
<v-icon dark #click="close()"> mdi-close-circle-outline </v-icon>
</v-btn>
</template>
</v-snackbar>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
#Component({})
export default class Snackbar extends Vue {
get snackbar() {
return this.$store.state.snackbar.show
}
set snackbar(show: boolean) {
this.$store.dispatch('updateSnackbar', { show, text: '' })
}
get text() {
return this.$store.state.snackbar.text
}
public close() {
this.$store.dispatch('updateSnackbar', { show: false, text: '' })
}
}
</script>

Resources