I have added the Quasar plugin onto an existing Vuejs project.
The problem I am experiencing is that when I use Quasar components, it seems that some of it's styling does not work.
Will provide pics at the bottom of the page.
In my main.js I use Quasar :
projectAuth.onAuthStateChanged(()=>{
if(!app){
app = createApp(App).use(Quasar, quasarUserOptions).use(store).use(router).mount('#app')
}
})
quasarUserOptions:
import './styles/quasar.sass'
import '#quasar/extras/material-icons/material-icons.css'
// To be used on app.use(Quasar, { ... })
export default {
config: {},
plugins: {
}
}
and finally the component in which I wish to use a Quasar component:
<template>
<div id="q-app" style="min-height: 100vh;">
<div class="q-pa-md">
<q-select
filled
hide-bottom-space
options-dense
hide-dropdown-icon
v-model="model"
use-input
clearable
multiple
input-debounce="500"
:options="filterOptions"
#filter="filterFn"
style="width: 200px"
counter
hint="Selected items"
label="Actors"
dense
hide-selected
></q-select>
</div>
</div>
<template/>
<script >
import {ref} from 'vue'
export default {
setup(){
const stringOptions = ['Google', 'Facebook', 'Twitter', 'Apple', 'Oracle']
const filterOptions = ref(stringOptions)
const model = ref(null)
const filterFn = (val, update) => {
update(() => {
if (val === '') {
filterOptions.value = stringOptions
}
else {
const needle = val.toLowerCase()
filterOptions.value = stringOptions.filter(
v => v.toLowerCase().indexOf(needle) > -1
)
}
})
}
return {filterFn, model, filterOptions, stringOptions}
}
}
</script>
<style scoped>
</style>
The following img is what It is supposed to look like and how it displays in codepen :
The following img is what It looks like in my vuejs app :
Both images aren't at the same scale but that is not the problem.
Any advice will be very appreciated
Related
I am trying to use Cypress for component testing in my Vue app. I am using the vue-i18n library to provide localisation for the app. When attempting to test the rendering of my loading spinner component, I am getting the following error from the vue-i18n library:
SyntaxError: Need to install with `app.use` function
at createCompileError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:183:17)
at createI18nError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:2625:10)
at useI18n (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:4231:11)
Previously to this, I was getting an error from Pinia. I resolved this by adding the following to cypress/support/component.ts:
import { createPinia, setActivePinia } from 'pinia';
setActivePinia(
createPinia()
);
My LoadingSpinner component code is as follows:
<script setup lang="ts">
import { computed } from "#vue/reactivity";
import { useLocaleStore } from "#/stores/locale";
//props
const { i18n } = useLocaleStore();
</script>
<template>
<div class="d-flex justify-content-center m-5">
<div
class="spinner-border text-primary"
:style="{ width, height }"
role="status"
>
<span class="visually-hidden">{{ i18n.t("loading") }}</span>
</div>
</div>
</template>
And the test code:
import LoadingSpinner from "../../src/components/ui/LoadingSpinner.vue";
describe("LoadingSpinner", () => {
it("renders", () => {
cy.mount(LoadingSpinner);
});
});
/stores/locale:
import { computed } from "vue";
import { defineStore } from "pinia";
import { useI18n } from "vue-i18n";
export const useLocaleStore = defineStore("locale", () => {
const i18n = useI18n({
useScope: "global",
inheritLocale: true,
});
const currentLocale = computed(() => i18n.locale);
const locales = computed(() => i18n.availableLocales);
return { i18n, currentLocale, locales };
});
I found this github release that implies I need to add vue-i18n as a plugin to the mount() call, but I can't work out how to do it. Does anyone know a solution?
I'm trying to implement vue-phone-input by wrapping it with a Quasar q-field.
It's mostly working. The input works fine and it shows validation errors underneath the input.
The problem is that I can submit the form even if there is a validation error.
How do I prevent this from happening?
Normally when using a q-form with a q-input and q-btn it will automatically stop this from happening.
So why doesn't it work here with q-field and vue-tel-input?
<template>
<q-form #submit="handlePhoneSubmit">
<q-field
v-if="isEditingPhone"
autocomplete="tel"
label="Phone"
stack-label
:error="isPhoneError"
error-message="Please enter a valid phone number."
outlined
hide-bottom-space
>
<vue-tel-input
v-model="phoneInput"
#validate="isPhoneError = !isPhoneError"
></vue-tel-input>
</q-field>
<q-btn
color="primary"
text-color="white"
no-caps
unelevated
style="max-height: 56px"
type="submit"
label="Save"
#submit="isEditingPhone = false"
/>
</q-form>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue';
import { VueTelInput } from 'vue-tel-input';
import 'vue-tel-input/dist/vue-tel-input.css';
const phone: Ref<string | null> = ref('9999 999 999');
const isEditingPhone = ref(true);
const isPhoneError = ref(false);
const phoneInput: Ref<string | null> = ref(null);
const handlePhoneSubmit = () => {
phone.value = phoneInput.value;
console.log('Form Saved');
};
</script>
First, you should use the :rules system from Quasar instead of :error and #validate
<q-field :rules="[checkPhone]"
function checkphone(value: string) {
return // validate the value here
}
Then, if the submit doesn't suffice, you may need to set a ref on your <q-form, then call its validate() method.
Here how to do it (I removed parts of the code to highlight what's required).
<template>
<q-form ref="qform" #submit="handlePhoneSubmit">
//..
</q-form>
</template>
<script setup lang="ts">
import { QForm } from "quasar";
import { ref } from "vue";
//..
const qform = ref<QForm|null>(null);
async function handlePhoneSubmit() {
if (await qform.value?.validate()) {
phone.value = phoneInput.value;
}
}
I'm trying to run jest-snapshot tests in a Vue application which has a Component with router-link element.
I'm using RouterLinkStub as recommended in Snapshot Testing with Vue-Router
My component template is something like
<template>
<section class="content">
<div>
<router-link to="/" v-slot="{ href, route, navigate }">
<a :href="href" #click="navigate">
Homepage
</a>
</router-link>
</div>
</section>
</template>
The problem is that in snapshot, the router-link renders as empty anchor element (<a />), so if I change the link text the snapshot test stills pass.
This is my snapshot test:
import { RouterLinkStub, shallowMount } from '#vue/test-utils'
import PageNotFound from 'views/PageNotFound.vue'
describe('PageNotFound.vue', () => {
it('renders as expected', () => {
const wrapper = shallowMount(PageNotFound, {
stubs: {
RouterLink: RouterLinkStub
}
})
expect(wrapper.element).toMatchSnapshot()
})
})
Can you help me please?
Thanks
With Vue 3
import { shallowMount, RouterLinkStub } from '#vue/test-utils'
import router from '../../router'
const findComponentRouterLink = () =>
wrapper.findComponent(RouterLinkStub)
const wrapper = shallowMount(Component, {
global: {
plugins: [router],
stubs: {
RouterLink: RouterLinkStub,
},
},
})
findComponentRouterLink().text()).toBe('Link text')
I was given this problematic codebase, where the Vue components aren't loading in.
Vue is mounting, but without any components.
This is a Laravel 5.7 app, using blade templates with some Vue added in.
This is the initial code:
import 'babel-polyfill'
import loadClientScripts from './load-client-scripts'
import 'bootstrap-material-design/js/'
// Vue & axios
import Vue from 'vue'
import { axios } from '../axios-config'
import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm'
import { createLocales } from '../vue-i18n-config'
import Noty from 'noty'
//Components
import signInForm from './components/SignInForm'
import signUpForm from './components/SignUpForm'
import forgotPassForm from './components/ForgotPassForm'
// import RegisterToAgency from './components/RegisterToAgency'
import SendEmailForm from './components/SendEmailForm'
import AgencyServiceCategories from './components/AgencyServiceCategories'
import DropdownWithCheckboxes from './components/DropdownWithCheckboxes'
import LasiCoalitionAgencies from './components/LasiCoalitionAgencies'
import ServiceProviders from "./components/ServiceProviders";
import ServiceProvider from "./components/ServiceProvider";
import vSelect from "vue-select";
window.axios = axios
Vue.component('v-select', vSelect)
// Bootstrap Vue
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n
}).$mount('#app')
}
sign in form component for example:
<template>
<div>
<b-form
id="sign-in-form"
#submit="onSubmit"
>
<div class="form-group">
<b-form-input
id="sgi-email"
v-model="model.email"
required
name="email"
:state="state('email')"
type="email"
:placeholder="$t('validation.attributes.email_address')"
/>
<b-form-feedback>{{ feedback('email') }}</b-form-feedback>
</div>
<div class="form-group mb-3">
<b-form-input
id="sgi-password"
v-model="model.password"
required="required"
name="password"
:state="state('password')"
type="password"
:placeholder="$t('validation.attributes.password')"
/>
<b-form-feedback>{{ feedback('password') }}</b-form-feedback>
</div>
<div class="form-group my-0">
<a
class="text-opacity forgot-pass-link"
href="#"
>
{{ $t('labels.user.password_forgot') }}
</a>
</div>
</b-form>
</div>
</template>
<script>
console.log('IM HIT')
export default {
name: 'SignInForm',
data() {
return {
model: {
email: '',
password: ''
},
validation: {}
}
},
mounted() {
this.test()
},
methods: {
test() {
console.log("test")
},
feedback(name) {
if (this.state(name)) {
return this.validation.errors[name][0]
}
},
state(name) {
return this.validation.errors !== undefined &&
this.validation.errors.hasOwnProperty(name)
? 'invalid'
: null
},
onSubmit(evt) {
evt.preventDefault()
window.axios
.post('/login', this.model)
.then(response => {
location.href = '/app'
})
.catch(e => {
if (e.response.status === 422) {
this.validation = e.response.data
return
}
})
}
}
}
</script>
Any ideas help!
in the example sign in form, the console does output the "Im hit" that I had placed to ensure that things were loaded.
Thanks
Are you at any point rendering anything with that Vue instance?
Try passing a component to its render function like so:
// lets pretend you've imported a component from some file App.vue up here and called the component simply 'App'
// e.g.: const App = require('./App.vue') or import App from './App.vue';
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n,
render: createElement => createElement(App) // needs a render function
}).$mount('#app')
}
I am trying to integrate Phaser 3 with Vue.js 2.
My goal is to create a Vue.js component associated with a game canvas.
My initial solution was:
<template>
<div :id="id">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
id: null,
game: null
}
},
mounted () {
this.id = 'game' + this._uid
var config = {
parent: this.id,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
This code attach the game canvas to my template. But to my surprise it only worked 'sometimes'.
I figured out, after hours of debugging, that my div element in the DOM wasn't updated with the id when I was instantiating my new Game.
So I came up with the solution of instantiating the id in the beforeMount () method as follow:
<template>
<div :id="id">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
id: null,
game: null
}
},
beforeMount () {
this.id = 'game' + this._uid
},
mounted () {
var config = {
parent: this.id,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
It is working, but I would like to know if there is a more simple and elegant solution ?
One better solution for integrating Phaser.Game into the application is directly passing the config the HTML element, a configuration supported by Phaser.Game.
To get a reference to a HTML element in vue, you can use refs, these are basically id's, but local to the component itself, so there is not risk in creating conflicts.
<template>
<div ref="myDiv">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
game: null
}
},
mounted () {
var config = {
parent: this.$refs.myDiv,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
Vue3 sample:
<script setup>
import { ref,onMounted } from 'vue';
import Phaser from 'phaser'
const myDiv = ref(null)
let canvasWidth = 750;
let canvasHeight = 1450;
onMounted(() => {
const config = {
type: Phaser.AUTO,
parent: popWrap.value,
width: canvasWidth,
height: canvasHeight,
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
})
</script>
<template>
<div ref="myDiv">
</div>
</template>