No http lib found to perform ajax request in VJSF - ajax

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
}

Related

How to integrate Stripe Checkout in Laravel Vue app

I am trying to integrate Stripe Checkout into my Laravel Vue.js application. I watched a tutorial on YouTube. However, I get the following error.
Vue Stripe will not work on an insecure host. Make sure that your site
is using TCP/SSL.
<template>
<div class="py-8 flex justify-center">
<stripe-checkout
ref="checkoutRef"
mode="payment"
:pk="publishableKey"
:sessionId="sessionId"
/>
<button class=" bg-blue-500 px-2 py-1 rounded text-white" #click="submit">Pay now!</button>
</div>
</template>
<script>
import { StripeCheckout } from "#vue-stripe/vue-stripe";
import axios from "axios";
export default {
components: {
StripeCheckout ,
},
data() {
return {
publishableKey: "pk_test_51KtYynFJTg08EEU2sYHLN0LKrnZTuJCazai8jmokQ2096V7IXYjX2XsdGi7xh5jOgSCz5nnn7YfJS5afTtEHRSxk00EUEcmhsj",
sessionId: null,
};
},
mounted() {
console.log("Component mounted.");
this.getSession()
},
methods: {
getSession() {
axios.get('getSession').then(res => {
this.sessionId = res.data.id
}).catch(err => {
});
},
submit () {
this.$refs.checkoutRef.redirectToCheckout();
}
}
};
</script>```
According to the docs you can register it as a plugin where you can define a testMode option to override the insecure host warning
import Vue from 'vue';
import { StripePlugin } from '#vue-stripe/vue-stripe';
const options = {
pk: process.env.STRIPE_PUBLISHABLE_KEY,
testMode: true, // Boolean. To override the insecure host warning
stripeAccount: process.env.STRIPE_ACCOUNT,
apiVersion: process.env.API_VERSION,
locale: process.env.LOCALE,
};
Vue.use(StripePlugin, options);

Vuejs custom Modal event bus is not firing

I have created my own custom Modal plugin in vuejs to be added to my Laravel 8 app. The problem I am facing is opening the modal.
I have created the plugin in my app.js file
const Modal = {
install (Vue) {
this.event = new Vue()
Vue.prototype.$modal = {
show (modal, params = {}) {
Modal.event.$emit('show', modal, params)
},
$event: this.event
}
}
}
Vue.use(Modal)
I have created two vue components for my modal
<!-- AppModal //-->
<template>
<transition name="modal">
<div v-if="visible">
<div class="app-modal" #click.prevent="$modal.hide(name)"></div>
<div class="app-modal-inner">
close
<slot name="body" :params="params"/>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "AppModal",
data () {
return {
params: {},
visible: false,
}
},
props: {
name: {
required: true,
type: String,
}
},
methods: {
setVisible () {
this.visible = true
},
setHidden () {
this.visible = false
}
},
beforeMount() {
this.$modal.$event.$on('show', (modal, params) => {
if (this.name !== modal) {
return
}
this.params = params
this.setVisible()
})
},
}
</script>
<!-- AppNonMemberRegisterModal //-->
<template>
<app-modal name="register">
<template slot="header">
<h1 class="text-lg-left text-4xl border border-b-2">Register Now</h1>
</template>
<template slot="body" slot-scope="{ params }">
<p>You need to register in order to share, comment and like on the site</p>
</template>
</app-modal>
</template>
<script>
import AppModal from "../AppModal";
export default {
name: "AppNonMemberRegisterModal",
components: { AppModal },
}
</script>
Where I am firing the event I import the AppNonMemberRegisterModal and I have the following click event: #click.prevent="$modal.show('register')".
When the following code is reached Modal.event.$emit('show', modal, params) I get the following error messages in my cnosole console.log(Modal)
Vue 3 removes the Event API (i.e., $on, $off, etc.). The migration guide recommends using tiny-emitter to create your own event bus. That example shows how to create a global bus, but it seems your plugin just needs a local bus, which you could create like this:
// eventBus.js
import Emitter from 'tiny-emitter'
export function createEventBus() {
const emitter = new Emitter()
return {
$on: (...args) => emitter.on(...args),
$once: (...args) => emitter.once(...args),
$off: (...args) => emitter.off(...args),
$emit: (...args) => emitter.emit(...args)
}
}
Then in your plugin, create a global with app.config.globalProperties, referring to the locally created event bus:
// myPlugin.js
import { createEventBus } from './eventBus'
export default {
install(app) {
const eventBus = createEventBus()
app.config.globalProperties.$modal = {
show (modal, params = {}) {
eventBus.$emit('show', modal, params)
},
$event: eventBus
}
}
}
And install it:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import myPlugin from './myPlugin'
createApp(App).use(myPlugin).mount('#app')
Also be aware that your slot usage in AppRegisterModal.vue needs to be updated to the latest syntax (v-slot or # shorthand):
<app-modal name="register">
<!--
<template slot="header">
-->
<template #header>
<!--
<template slot="body" slot-scope="{ params }">
-->
<template #body="{ params }">
</app-modal>
demo

How to display records from Laravel via Vuetify v-data-table component

I have a project build in Laravel with Vue.js which work perfect statically, but I need convert it into dynamically to pull records from database table to v-data-table component.
I know Laravel and I know How these things works via Ajax/jQuery but I'm pretty new in Vue.js
Can someone explain to me how to display the results from the database in the v-data-table component.
Thanks.
Here is the Vue.js file:
<template>
<v-app>
<v-main>
<div>
<v-tab-item>
<v-card flat>
<v-card-text>
<v-card-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="items"
:items-per-page="5"
class=""
:search="search">
</v-data-table>
</v-card-text>
</v-card>
</v-tab-item>
</div>
</v-main>
</v-app>
</template>
<script>
export default {
data: () => ({
search: '',
items: [],
headers: [
{
text: '#',
align: 'start',
sortable: false,
value: 'id',
},
{ text: 'Name', value: 'name' },
{ text: 'Slug', value: 'slug' },
],
/*THIS IS A STATIC DATA*/
// items: [
// {
// id: 1,
// name: 'Test Name 1',
// slug: 'test-name-1',
// },
// {
// id: 2,
// name: 'Test Name 2',
// slug: 'test-name-2',
// },
// ],
/*THIS IS A STATIC DATA*/
}),
created () {
this.getItems();
},
methods: {
getItems() {
axios
.get('/test/vue')
.then((response) => {
this.items = response.data,
console.log(response.data)
})
.catch(error => console.log(error))
},
}
}
</script>
And Here is Blade file:
#extends('it-pages.layout.vuetify')
#section('content')
<div id="appContainer">
<software-template></software-template>
</div>
Output in the console is :
console.log
Response from axios is also Ok
response
My Controller :
public function showData()
{
$items = Category::select('id', 'name', 'slug')->where('order', 1)->get();
// dd($items);
return response()->json(['items' => $items]);
}
My route:
Route::get('test/vue', 'PagesController#showData');
console.log after changes axios lines
console-log
So there were multiple issues here:
The backend did you return a correct array
The frontend performed a post request instead of a get
The this context is not correct since you are using a function instead of arrow syntax
Make sure to look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions and read about how this changes how this is elevated.
In your case, you need to change the code on the then part of your axios call:
.then((response) => {
this.items = response.data
})
I must to say that I solve the problem.
Problem was been in axios response.
Instead this.items = response.data I change to this.items = response.data.items and it work perfectly.
methods: {
getItems() {
axios
.get('/test/vue')
.then((response) => {
this.items = response.data.items
console.log(response.data.items)
})
.catch(error => console.log(error))
},
}

Vue - Vue routes doesn´t exist

I have a project in Laravel + Vue. In Laravel i have some routes for create endpoints and start page.
Laravel Routes
Route::get('/', 'Auth\LoginController#showLoginForm');
Route::post('/login', 'Auth\LoginController#login');
Auth::routes();
Route::resource('gateways', 'GatewayController');
Route::resource('contadores', 'ContadorController');
'/' route go to Blade file with Login Component.
Login Component has this code.
<template>
<v-content slot="content">
<v-container class="fill-height" fluid>
<v-row align="center" justify="center">
<v-col cols="12" md="8">
<v-card class="elevation-12">
<v-toolbar dark flat>
<v-toolbar-title>LoRaWAN</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form>
<v-text-field
label="Usuario"
name="username"
prepend-icon="mdi-account"
type="text"
v-model="username"
/>
<v-text-field
label="Contraseña"
name="password"
prepend-icon="mdi-key"
:append-icon="value ? 'mdi-eye' : 'mdi-eye-off'"
#click:append="() => (value = !value)"
:type="value ? 'password' : 'text'"
v-model="password"
/>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn block dark #click="submit()">Entrar</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</v-content>
</template>
<script>
export default {
data() {
return {
value: String,
username: "",
password: ""
};
},
methods: {
submit() {
axios
.post("http://127.0.0.1:8000/login", {
username: this.username,
password: this.password
})
.then(response => {
if (response.data.token != null) {
localStorage.setItem("token", response.data.token);
console.log("ok");
this.$router.push({
name: "lora",
params: { user: this.username }
});
}
})
.catch(function(errors) {
let error = errors.response.data.errors;
let mensaje = "Error no identificado";
if (error.hasOwnProperty("username")) {
mensaje = error.username[0];
} else {
mensaje = error.password[0];
}
Swal.fire({
title: "Error",
text: mensaje,
icon: "error",
confirmButtonText: "Ok"
});
});
}
}
};
</script>
As we can see when login endpoint return token we want to push to other 'lora' route.
Vue routes file
import ContadorComponent from "./components/contador/ContadorComponent.vue";
import GatewayComponent from "./components/gateway/GatewayComponent.vue";
import HomeComponent from "./components/home/HomeComponent.vue";
import MainComponent from "./components/main/MainComponent.vue";
const routes = [{
path: "/lora",
name: "lora",
component: MainComponent,
props: true,
children: [{
path: "",
name: "home",
component: HomeComponent
},
{
path: "contadores",
name: "contadores",
component: ContadorComponent
},
{
path: "gateways",
name: "gateways",
component: GatewayComponent
}
]
}];
const router = new VueRouter({
mode: 'history',
routes: routes
});
new Vue({
vuetify: new Vuetify(),
router
}).$mount("#app");
And lora route (Main Component)
<template>
<v-app id="app">
<layoutDrawer></layoutDrawer>
<layoutHeader></layoutHeader>
<v-content>
<router-view></router-view>
</v-content>
<layoutFooter></layoutFooter>
</v-app>
</template>
<script>
import layoutHeader from "./partials/HeaderComponent.vue";
import layoutFooter from "./partials/FooterComponent.vue";
import layoutDrawer from "./partials/SidebarComponent.vue";
export default {
props: {
username: { type: String, default: "Invitado" }
},
components: {
layoutHeader,
layoutDrawer,
layoutFooter
}
};
</script>
The problem: If i go to http://127.0.0.1:8000/lora returns that this route doesn´t exist. In the vue routes file i declare it, so i don´t know why returns this. Maybe Laravel generate a conflict or something with routes. In laravel routes file i test this code and works
Route::get('/test', function () {
return view('home');
})->name('home');
The view home is blade file with Main Component. Maybe something happens with the vue routes that project doesn't recognize and only works Laravel routes..
The question: Are the vue routes properly declares? Anybody see some error?
Your client and server are running on the same port: http://127.0.0.1:8000.
The url for your lora route should be something like http://127.0.0.1:8001/lora
I found a partially solution. In Laravel routes i need to put this
Route::get('{any?}', function () {
return view('layout');
})->where('any', '.*');
Every time the user push to another page load Layout blade.
#extends('layouts.app')
#section('content')
<layout-component></layout-component>
#endsection
Layout Component
<template>
<v-app id="app">
<router-view></router-view>
</v-app>
</template>

Vue.js input validation w VeeValidate - How to avoid onInput action while invalid

I am validating a input field ( required , min length 3 ) with VeeValidate plugin.. it's working fine
but how I can avoid the onInput action to be called ( to avoid commit in store when the input becomes invalid ( as soon as input aria-invalid switch to false )
shortly said : Is there anyway to switch calling/not Calling onInput: 'changeTitle' when the input field aria-invalid is false/true ?
thanks for feedback
ChangeTitleComponent.vue
<template>
<div>
<em>Change the title of your shopping list here</em>
<input name="title" data-vv-delay="1000" v-validate="'required|min:3'" :class="{'input': true, 'is-danger': errors.has('required') }" :value="title" #input="onInput({ title: $event.target.value, id: id })"/>
<p v-show="errors.has('title')">{{ errors.first('title') }}</p>
</div>
</template>
<style scoped>
</style>
<script>
import { mapActions } from 'vuex'
import Vue from 'vue'
import VeeValidate from 'vee-validate'
Vue.use(VeeValidate)
export default {
props: ['title', 'id'],
methods: mapActions({ // dispatching actions in components
onInput: 'changeTitle'
})
}
</script>
vuex/actions.js
import * as types from './mutation_types'
import api from '../api'
import getters from './getters'
export default {
...
changeTitle: (store, data) => {
store.commit(types.CHANGE_TITLE, data)
store.dispatch('updateList', data.id)
},
updateList: (store, id) => {
let shoppingList = getters.getListById(store.state, id)
return api.updateShoppingList(shoppingList)
.then(response => {
return response
})
.catch(error => {
throw error
})
},
...
}
UPDATE
I tried to capture the input value with #input="testValidation) and check for a valid input value (required)
if valid ( aria-invalid: false) then I emit the input value, but the props are not updated in the parent component and the vuex action 'changeTitle' is not triggered
<template>
<div>
<em>Change the title of your shopping list here</em>
<input name="title" ref="inputTitle" data-vv-delay="1000" v-validate="'required'" :class="{'input': true, 'is-danger': errors.has('required') }" :value="title" #input="testValidation({ title: $event.target.value, id: id })"/>
<p v-show="errors.has('title')">{{ errors.first('title') }}</p>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import Vue from 'vue'
import VeeValidate from 'vee-validate'
Vue.use(VeeValidate)
export default {
props: ['title', 'id'],
methods: {
testValidation: function (value) {
const ariaInvalid = this.$refs.inputTitle.getAttribute('aria-invalid')
if (ariaInvalid === 'false') {
this.$nextTick(() => {
this.$emit('input', value) // should change props in parent components
})
} else {
console.log('INVALID !') // do not change props
}
},
...mapActions({
onInput: 'changeTitle' // update store
})
}
}
</script>
like you access the errors collection in the VUE template, you can also access the same errors collection in your testValidation method
so replace
const ariaInvalid = this.$refs.inputTitle.getAttribute('aria-invalid')
with
const ariaInvalid = this.$validator.errors.has('title')
grtz

Resources