Vue components not loading - vue.js

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')
}

Related

Cypress component testing with Vue-I18N

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?

Using Pinia with Vue.js Web components

Question has been updated
I'm trying to use a Pinia's store with web components created with Vue.js but I have this error in the console:
[Vue warn]: injection "Symbol(pinia)" not found at <HelloWorld.ce msg="message" >
I have a dead simple exemple.
main.ts
import { defineCustomElement } from 'vue'
import HelloWorld from './components/HelloWorld.ce.vue'
const ExampleElement = defineCustomElement(HelloWorld)
customElements.define('hello-world', ExampleElement)
store.ts
import { defineStore, createPinia, setActivePinia } from "pinia";
setActivePinia(createPinia());
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++;
},
},
});
HelloWorld.ce.vue
<script setup lang="ts">
import { ref } from 'vue'
import { useCounterStore } from '../store.ts'
defineProps<{ msg: string }>()
const store = useCounterStore()
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" #click="store.increment()">count is {{ store.counter }}</button>
</div>
</template>
You're recreating pinia in the store after already creating it in main.js. Remove these lines from your store:
import { createPinia } from 'pinia'
const pinia = createPinia()

Vue vuelidate $v is not defined

I am new to vue js my main.js file looks like this
import { createApp } from 'vue'
import App from './App.vue'
import router from './routes.js'
import Vuelidate from 'vuelidate'
createApp(App).use(Vuelidate)
createApp(App).use(router).mount('#app')
and my form components looks like this
<template>
<form methos="post" v-on:submit.prevent="submit">
<input type="text" v-model.trim="$v.indate.$model" :class="{'is-invalid':
validationStatus($v.indate)}" class="form-control mt-2 mb-2" placeholder="DD/MM/YYYY">
<div v-if="!$v.indate.required" class="invalid-feedback">Inward date is required.</div>
</form>
</template>
<script>
import {required} from 'vuelidate/lib/validators'
export default {
name: 'FormInward',
validations: {
indate: {required}
},
data: function(){
return {
indate: ''
}
},
methods: {
validationStatus: function(validation){
return typeof validation != "undefined" ? validation.$error : false;
},
submit: function() {
this.$v.$touch();
if (this.$v.$pendding || this.$v.$error) return;
console.log(this.$v);
}
}
}
</script>
but i cant use the vuelidate it shows error like "Property "$v" was accessed during render but is not defined on instance."
when i check with "console.log(this.$v)" - it shows "undefined"

Watch doesn't get fire/trigger when global property change

I’m very new to Vue and I begin with Vue 3. I was trying to migrate a template from Vue 2 to Vue 3 so I can start with my project.
I have this plugin file.
Sidebar\Index.ts
import SidebarPlugComp from './SidebarPlugComp.vue'
import SidebarLinkPlugComp from './SidebarLinkPlugComp.vue'
// tiny internal plugin store
const SidebarStore = {
showSidebar: false,
sidebarLinks: [],
displaySidebar (value: boolean) {
this.showSidebar = value
}
}
const SidebarPlugin = {
install (app: any) {
app.config.globalProperties.$sidebar = SidebarStore
app.component('side-bar-plug-comp', SidebarPlugComp)
app.component('sidebar-link-plug-comp', SidebarLinkPlugComp)
}
}
export default SidebarPlugin
Also I have a BaseTopNavLay layout file so I can toggle the sidebar with handleSidebarToggle onclick button method
<template>
\\...
<div class="navbar-toggle d-inline" :class="{toggled: $sidebar.showSidebar}">
<button type="button"
class="navbar-toggler"
aria-label="Navbar toggle button"
#click.prevent="handleSidebarToggle">
<span class="navbar-toggler-bar bar1"></span>
<span class="navbar-toggler-bar bar2"></span>
<span class="navbar-toggler-bar bar3"></span>
</button>
\\ ...
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ModalComp } from '../components'
export default defineComponent({
name: 'BaseTopNavLay',
components: {
ModalComp
},
// ...
methods: {
handleSidebarToggle (): void {
this.$sidebar.displaySidebar(!this.$sidebar.showSidebar)
},
handleHideSideBar (): void {
this.$sidebar.displaySidebar(false)
},
}
})
</script>
And here is the watch in the App.vue file
<template>
<component :is="this.$route.meta.layout || 'div'">
<router-view />
</component>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Application',
methods: {
toggleNavOpen () {
console.log('here')
let root = document.getElementsByTagName('html')[0]
root.classList.toggle('nav-open')
}
},
/*watch: {
'$sidebar.showSidebar': function(newVal, oldVal) {
console.log(newVal, oldVal)
}
}*/
mounted () {
//#ts-ignore
this.$watch('$sidebar.showSidebar', this.toggleNavOpen)
}
})
</script>
Wherever I test the var this.$sidebar.showSidebar I can access to its value properly. Also, the onclick method is changing the SidebarStore object in Sidebar\Index.ts plugin file.
Can anyone give me a hint what am I missing here? Why the watch doesn't get fired. Thanks in advance.
The problem is that you have not made your $sidebar reactive, and a watch needs to use a reactive variable.
You can keep the store where you have it, but I'd put it into a separate file (store.js) and import where needed, no need to put it on app.config.globalProperties.$sidebar (but that might be a personal preference
// store.js
// using reactive (all values)
export const SidebarStore = Vue.reactive({
showSidebar: false,
sidebarLinks: [],
})
// or using ref (one for each)
// export const showSidebar = Vue.ref(false);
export const displaySidebar = (value: boolean) => {
SidebarStore.showSidebar.value = value;
}
this will make SidebarStore and displaySidebar available anywhere in your code
use like this
<template>
\\...
<div class="navbar-toggle d-inline" :class="{toggled: $sidebar.showSidebar}">
<button type="button"
class="navbar-toggler"
aria-label="Navbar toggle button"
#click.prevent="handleSidebarToggle">
<span class="navbar-toggler-bar bar1"></span>
<span class="navbar-toggler-bar bar2"></span>
<span class="navbar-toggler-bar bar3"></span>
</button>
\\ ...
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ModalComp } from '../components'
import { SidebarStore, displaySidebar } from '../store'
export default defineComponent({
name: 'BaseTopNavLay',
components: {
ModalComp
},
// ...
methods: {
handleSidebarToggle (): void {
displaySidebar(!SidebarStore.showSidebar)
},
handleHideSideBar (): void {
displaySidebar(false)
},
}
})
</script>

Error in Unit Test VueJS component with Vuex

Error: undefined is not a constructor (evaluating 'vm.$el.querySelector('h3')')
Follow my code and full code here
// main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import 'babel-polyfill'
require('es6-promise').polyfill()
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
// Home.vue
<template>
<div>
<h3 class="ui center aligned header">
An simple contact list
</h3>
<div class="ui fluid input">
<input type="text" placeholder="Search..." v-model="searchTerm" autofocus autocomplete="false">
</div>
<div class="ui inverted dimmer" :class="{ 'active': isFetchingContacts }">
<div class="ui text loader">Loading contacts</div>
</div>
<transition-group name="custom" mode="out-in"
enter-active-class="animated flipInX"
class="ui middle aligned selection celled relaxed list">
<component class="item"
v-for="contact in contacts" :key="contact.id"
:is="openedId === contact.id ? 'contact-card' : 'contact-item'" :contact=contact>
</component>
</transition-group>
<div class="ui warning message" v-if="contacts.length <= 0">
<div class="header">
No contacts found!
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex'
import _ from 'lodash'
import contactItem from './ContactItem'
import contactCard from './ContactCard'
export default {
name: 'home',
data () {
return { searchTerm: '' }
},
created () { this.fetchContacts() },
watch: {
'$route': 'fetchContacts',
'searchTerm': 'search'
},
computed: {
...mapGetters([ 'contacts' ]),
...mapState({
isFetchingContacts: state => state.isFetchingContacts,
openedId: state => state.openedId
})
},
methods: {
...mapActions([ 'fetchContacts' ]),
search: _.debounce(function () {
this.fetchContacts(this.searchTerm)
}, 900)
},
components: { contactItem, contactCard }
}
</script>
// Home.spec.js
import Vue from 'vue'
import Home from '#/components/Home'
describe('Home.vue', () => {
it('should render title', () => {
const vm = new Vue(Home).$mount()
expect(vm.$el.querySelector('h3'))
.toBe('An simple contact list')
})
})
Console error
Home.vue
✗ should render title
undefined is not a constructor (evaluating 'vm.$el.querySelector('h3')')
webpack:///test/unit/specs/Home.spec.js:7:32 <- index.js:30622:32
ERROR LOG: '[Vue warn]: Error in created hook:
(found in <Root>)'
ERROR LOG: TypeError{stack: 'mappedAction#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:10798:25
boundFn#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:690:16
created#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:31008:23
callHook#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:2786:25
_init#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:4209:13
VueComponent#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:4373:17
http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:30627:29
callFnAsync#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4470:25
run#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4420:18
runTest#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4936:13
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:5042:19
next#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4853:16
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4863:11
next#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4787:16
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4831:9
timeslice#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:82:27', line: 10798, sourceURL: 'http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644'}
ERROR LOG: '[Vue warn]: Error in render function:
you need to include a store, in order to get your HTML rendered.
import Vuex from 'vuex'
const store = new Vuex.Store()
const Component = Vue.extend(Home)
const vm = new Component({store}).$mount()