retrieving and storing Events in database to and from FullCalendar 5 with laravel 9, Vue 3, Breeze, Inertia, and Ziggy - laravel

I'm having quite a tussle with my code. I've deployed FullCalendar v5 as a Vue 3 component inside a laravel 9 site using Breeze for authentication and Inertia for speedy rendering. I have the Calendar/Pages/Index.vue displaying the calendar and updating events on the calendar with modals from the example, but I cannot manage to get the modals to create events in the database, nor get the events from the database table to display on the calendar. I'm trying to avoid Ajax and use axios to retrieve a JSON feed.
Here's the part of my controller that creates the JSON feed:
public function showEvents(Request $request) {
$event = Event::get(['title','acronym','city','venue','value','start','end']);
return response()->json(["events" => $event]);
}
Here's what the JSON feed returns:
{"events":[{"title":"Test","acronym":"TST","city":"Denver","venue":"Big Venue","value":"$0","start":"2022-10-25 00:00:00","end":"2022-10-28 00:00:00"}]}
And here's my rather massive Calendar/Index.vue:
<template>
<head title="Dashboard" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Resource Calendar Timeline
</h2>
</template>
<div class="py-12">
<div class="max-w-10xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-12 bg-white border-b border-gray-200">
<!--start calendar-->
<div class='demo-app'>
<div class='demo-app-main'>
<FullCalendar
class='demo-app-calendar'
:events="calendarEvents"
:options='calendarOptions'>
<template v-slot:eventContent='arg'>
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
</div>
</div>
<!--end calendar-->
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
<!--start calendar-->
<script setup lang='ts'>
import BreezeAuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { head, Link } from '#inertiajs/inertia-vue3';
import '#fullcalendar/core/vdom'; // solves problem with Vite
import { defineComponent } from 'vue';
import FullCalendar, { CalendarOptions, EventApi, DateSelectArg, EventClickArg } from '#fullcalendar/vue3';
import dayGridPlugin from '#fullcalendar/daygrid';
import timeGridPlugin from '#fullcalendar/timegrid';
import listPlugin from '#fullcalendar/list';
import interactionPlugin from '#fullcalendar/interaction';
import axios from 'axios';
</script>
<script lang='ts'>
const Demo = defineComponent({
components: {
FullCalendar,
},
data() {
return {
// trying to pull events from DB json -->>
eventSources: [{
url: 'https://l9-v3-breeze-crud.ddev.site/show-events', // use the `url` property
color: 'yellow', // an option!
textColor: 'black' // an option!
}],
calendarEvents: [{
events(title, start, end, callback) {
axios.get('https://l9-v3-breeze-crud.ddev.site/show-events').then(res => {
callback(res.data.events)
})
},
failure: function() {
alert('there was an error while fetching events!');
},
color: 'red',
textColor: 'white',
}],
//end events pull from DB -->
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
listPlugin,
interactionPlugin // needed for dateClick
],
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
},
initialView: 'dayGridMonth',
events: this.getEvents,
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: this.handleDateSelect,
eventClick: this.handleEventClick,
eventsSet: this.handleEvents
} as CalendarOptions,
currentEvents: [] as EventApi[],
}
},
methods: {
getEvents(info, successCallback, failureCallback) {
events(start, end, timezone, callback) {
axios.get('http://localhost:8000/show-events').then(res => {
callback(res.data.eventList)
})
})
},
handleWeekendsToggle() {
this.calendarOptions.weekends = !this.calendarOptions.weekends // update a property
},
handleDateSelect(selectInfo: DateSelectArg) {
let title = prompt('Please enter a new title for your event')
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay
})
}
},
handleEventClick(clickInfo: EventClickArg) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
},
handleEvents(events: EventApi[]) {
this.currentEvents = events
},
}
})
export default Demo
</script>
<style lang='css'>
h2 {
margin: 0;
font-size: 16px;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
}
li {
margin: 1.5em 0;
padding: 0;
}
b { /* used for event dates/times */
margin-right: 3px;
}
.demo-app {
display: flex;
min-height: 100%;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
.demo-app-sidebar {
width: 300px;
line-height: 1.5;
background: #eaf9ff;
border-right: 1px solid #d3e2e8;
}
.demo-app-sidebar-section {
padding: 2em;
}
.demo-app-main {
flex-grow: 1;
padding: 3em;
}
.fc { /* the calendar root */
max-width: 1200px;
margin: 0 auto;
}
.fc-day-today
{
background-color: var(--fc-today-bg-color, rgba(255, 220, 40, 0.15));
}
.fc-day-sat, .fc-day-sun
{
background-color: var(--fc-today-bg-color, rgba(255, 100, 40, 0.15));
}
</style>
I'm stumped! Either I pull an empty array for events with
events[]
or I get a 500 error denoting that "Module source URI is not allowed in this document: “http://[::1]:5173/resources/js/Pages/Calendar/Index.vue" when I try the axios.get URL
and, not matter what I try, I can't seem to get it to pull the 'calendarEvents' options from
calendarEvents: [{
events(title, start, end, callback) {
axios.get('https://l9-v3-breeze-crud.ddev.site/show-events').then(res => {
callback(res.data.events)
})
},
failure: function() {
alert('there was an error while fetching events!');
},
color: 'red',
textColor: 'white',
}],
Additional Information:
I've been continuing to look for a solution and found several online demo's that resolve the issue at hand, but none with my particular project dependencies. Being a newbie at this, I'm just not certain on how to merge the code. In particular, the structure of the Calendar/Index.vue file taken from the http://fullcalendar.io Vue3 and TypeScript demo here https://github.com/fullcalendar/fullcalendar-example-projects/tree/master/vue3-typescript And in the app.js file from this demo https://www.positronx.io/how-to-display-events-in-calendar-with-laravel-vue-js/
My app.js is complicated by the installation of Ziggy and Inertia and I'm not certain how to construct the Vue rather than a blade. Here's my app.js:
import './bootstrap';
import '../css/app.css';
//import { ZiggyVue } from 'ziggy-vue';
//import route from 'ziggy';
import { createApp, h } from 'vue';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });

Ok, I figured out how to use axios to retrieve events from the JSON feed. The issue wasn't in the app.js routes, but rather in the actual Vue component itself.
Previously unmentioned, here is the route that constructs the JSON feed of events:
Route::get('show-events', [CalendarController::class, 'showEvents']);
Here's the part of my controller that generates that JSON feed for events from the database:
public function showEvents(Request $request) {
$event = Event::get(['title','acronym','city','venue','value','start','end']);
return response()->json(["events" => $event]);
And here's the method part of the Vue component that retrieves events from the JSON feel and publishes them to the calendar:
methods: {
getEvents() {
axios.get('show-events')
.then(response => {
this.calendarOptions.events = response.data.events;
});
},

Related

How to prevent mui Tooltip from closing when pressed on date popper inside tooltip

I have a Tooltip that serves as a date filter. It has two different date inputs one is for from date and other is for to date, there are also two buttons one is to clear filters and other is to apply them. When I clear filter tooltip is closed, but I also want to close tooltip without clearing dates just by clicking outside of tooltip area. I have made it work but when I press on a date and open options to select date and press on that open popper, tooltip closes because it detects pooper as outside click.
My question is how to prevent outside click when selecting dates?
This is dates component that sits inside tooltip:
import { EtsButton } from '#components/utils/ets/buttons/ets-button/ets-button';
import { CustomDatePicker } from '#components/utils/ets/inputs/custom-input-date/custom-date-picker';
import {
mergeDateFiltersData,
setDateFiltersData
} from '#store/modules/modals-and-forms/actions';
import { useDispatch, useSelector } from '#utilsFn/hooks/use-selector';
import { useTranslation } from 'next-i18next';
import React from 'react';
import styled from 'styled-components';
export const DateFilters = (props: NTableColumnsFilter.IProps) => {
const { t } = useTranslation();
const fromDate = useSelector(
(s) => s?.modalsAndForms?.dateFiltersData?.fromDate
);
const toDate = useSelector((s) => s?.modalsAndForms?.dateFiltersData?.toDate);
const dispatch = useDispatch();
return (
<TableColumnsFilterMenu>
<div className="item">
<p className="label">{t('page-sales-analysis:::main::Date from')}</p>
<CustomDatePicker
value={fromDate || null}
onChange={(val) => {
dispatch(
mergeDateFiltersData({
fromDate: val
})
);
}}
/>
</div>
<div className="item">
<p className="label">{t('page-sales-analysis:::main::Date until')}</p>
<CustomDatePicker
value={toDate || null}
onChange={(val) => {
dispatch(
mergeDateFiltersData({
toDate: val
})
);
}}
/>
</div>
<div className="action-buttons">
<EtsButton
button={{
onClick: () => {
dispatch(
setDateFiltersData({
openModalType: null,
fromDate: null,
toDate: null
})
);
props.setFromDateHandler(null);
props.setToDateHandler(null);
props.setShowMenuHandler(false);
}
}}
height="Height33"
padding="Padding7x15"
color="grey"
background="whiteFFF"
fontStyle="bold14"
>
{t('page-sales-analysis:::main::Clear the filters')}
</EtsButton>
<EtsButton
button={{
onClick: () => {
dispatch(
mergeDateFiltersData({
openModalType: null
})
);
props.setFromDateHandler(fromDate || null);
props.setToDateHandler(toDate || null);
props.setShowMenuHandler(false);
}
}}
height="Height33"
padding="Padding7x15"
color="white"
background="redC20"
fontStyle="bold14"
>
{t('page-sales-analysis:::main::Filter')}
</EtsButton>
</div>
</TableColumnsFilterMenu>
);
};
export namespace NTableColumnsFilter {
export interface IProps {
setFromDateHandler: (val: Date | null) => void;
setToDateHandler: (val: Date | null) => void;
setShowMenuHandler: (val: boolean) => void;
}
}
const TableColumnsFilterMenu = styled.div`
border-radius: 7px;
background-color: #ffffff;
box-shadow: 0px 3px 6px #00000029;
padding: 16px 21px 20px 21px;
.label {
font: normal normal 600 14px/20px 'Open Sans';
color: #000000;
}
.item {
display: flex;
flex-direction: column;
}
.action-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 20px;
button:first-child {
margin-right: 10px;
}
button {
height: 33px;
}
}
`;
this is date filters tooltip component
import Tooltip, { TooltipProps } from '#mui/material/Tooltip';
import { SVGIconCalendar } from '#styles/global-icons/icons/svg-icon-calendar';
import { useOutsideClick } from '#utilsFn/hooks/use-outside-click';
import { useSelector } from '#utilsFn/hooks/use-selector';
import React from 'react';
import styled from 'styled-components';
import { DateFilters } from './components/date-filters';
interface ITooltipProps extends TooltipProps {
mobile: boolean | number;
}
const MenuTooltip = styled(({ className, ...props }: ITooltipProps) => (
<Tooltip
{...props}
classes={{ popper: className }}
componentsProps={{
tooltip: {
sx: {
backgroundColor: 'transparent',
padding: '0',
margin: '0',
minWidth: props?.mobile ? 200 : 419
}
}
}}
/>
))(() => ({}));
export const MenuDateTooltipFilter = (props: NTableColumnsFilter.IProps) => {
const isMobile = useSelector((s) => s.device.isMobile);
const refMenu = React.useRef<HTMLDivElement>(null);
const [showMenu, setShowMenu] = React.useState<boolean>(false);
const setShowMenuHandler = (val: boolean) => {
setShowMenu(val);
};
const handlerCloseMenu = () => {
setShowMenu(() => false);
};
useOutsideClick(refMenu, handlerCloseMenu);
return (
<ContainerTableColumnsFilter>
<MenuTooltip
mobile={isMobile ? 1 : 0}
placement="bottom-end"
open={showMenu}
title={
<div ref={refMenu}>
<DateFilters
setShowMenuHandler={setShowMenuHandler}
setFromDateHandler={props.setFromDateHandler}
setToDateHandler={props.setToDateHandler}
/>
</div>
}
>
<button
type="button"
className="icon-button"
onClick={() => {
setShowMenu(() => true);
}}
>
<SVGIconCalendar />
</button>
</MenuTooltip>
</ContainerTableColumnsFilter>
);
};
export namespace NTableColumnsFilter {
export interface IProps {
setFromDateHandler: (val: Date | null) => void;
setToDateHandler: (val: Date | null) => void;
}
}
const ContainerTableColumnsFilter = styled.div`
width: 50px;
height: 50px;
.icon-button {
cursor: pointer;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background-color: #ffffff;
:hover {
svg {
path {
fill: #c20b0b;
}
}
}
}
`;
Date picker itself is a customized mui datepicker
Update:
I have achieved my desired result by using onOpen and onClose event existing in on date picker and disabling outside click function when popper is open ,but each time i select date popper flickers for a second before closing
onOpen and OnClose
onOpen={() => {
props.setIsDatePickerOpensHandler(true);
}}
onClose={() => {
props.setIsDatePickerOpensHandler(false);
}}
type of the handler
setIsDatePickerOpensHandler: (val: boolean) => void;
state and handler function in menu-date-tooltip-filter.tsx
const [isDatePickerOpen, setIsDatePickerOpen] =
React.useState<boolean>(false);
const setIsDatePickerOpensHandler = (val: boolean) => {
setIsDatePickerOpen(val);
};
if date picker is not open allow the use of outside Click function
useOutsideClick(refMenu, () => {
if (!isDatePickerOpen) {
setShowMenu(() => false);
}
});
It looks like after selecting date popper, it gets rerendered several times before closing
My updated question is how to solve this flickering bug?

Webchat: unable to render new elements using activitymiddleware in combication with grouping of avatars

I am not able to add new elements to webchat using activitymiddleware since the update to 4.14. As an example I added some code I use for typing indicators. As soon as I do that, the grouping of my avatar does not work anymore.
Without activitymiddleware and showAvatarInGroup: 'sender' -> normal behavior. Avatar is shown once per group of messages as well as the timestamp.
With activitymiddleware and showAvatarInGroup: 'sender' -> typing indicators are rendered as expected, timestamps are shown (but not grouped) and the Avatar is not shown at all.
With activitymiddleware and showAvatarInGroup : 'group' -> typing indicators are rendered as expected and the Avatar as well as the timestamps are shown. But not grouped.
Botcode (Node)
The bot sends an event to ask the webchat client to render an element. In this case a typing indicator but it could also be a new inputform.
await context.sendActivity({ name: 'typingIndicator', type: 'event' });
The webchat code (React) has an activitymiddleware to render this typing indicator as soon as the event is in:
import React, { useEffect, useMemo } from 'react';
import ReactWebChat, { createDirectLine } from 'botframework-webchat';
import TypingIndicator from './TypingIndicator';
const WebChat = ({ className, onFetchToken, store, token }) => {
const directLine = useMemo(() => createDirectLine({ token }), [token]);
const activityMiddleware = () => next => ({ activity, nextVisibleActivity, ...otherArgs }) => {
const { name, type } = activity;
// first remove all existing typing indicators
let elements = document.getElementsByClassName('typing-indicator');
for (let i = 0; i < elements.length; i++) {
elements[i].style.display = 'none'
}
// if we reveive a typing event, render a typing Indicator
if (type === 'event' && name === 'typingIndicator') {
return () => <TypingIndicator activity={activity} nextVisibleActivity={nextVisibleActivity} />;
} else {
return next({ activity, nextVisibleActivity, ...otherArgs });
}
}
const styleOptions = {
botAvatarInitials: 'Max',
showAvatarInGroup: 'sender', // group works ok
botAvatarImage: `${process.env.REACT_APP_AVATAR_URL}`,
}
useEffect(() => {
onFetchToken();
}, [onFetchToken]);
return token ? (
<ReactWebChat className={`${className || ''} web-chat`} directLine={directLine} activityMiddleware={activityMiddleware} store={store} styleOptions={styleOptions}/>
) : (
<div className={`${className || ''} connect-spinner`}>
<div className="content">
<div className="icon">
<span className="ms-Icon ms-Icon--Robot" />
</div>
<p>Connecting.</p>
</div>
</div>
);
};
export default WebChat;
The typing indicator
import './TypingIndicator.css';
import React from 'react';
const {
hooks: { useRenderActivityStatus }
} = window.WebChat;
const TypingIndicator = ({ activity, nextVisibleActivity }) => {
const renderActivityStatus = useRenderActivityStatus({ activity, nextVisibleActivity });
return (
<div>
<div className="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
{renderActivityStatus()}
</div>
);
};
export default TypingIndicator
And its styling
.typing-indicator {
background-color: transparent;
height: 35px;
width: 60px!important;
border-radius: 20px;
padding:10px;
margin-left: 65px;
}
.typing-indicator span {
line-height: 35px;
display:inline-block;
vertical-align: middle;
height: 10px;
width: 10px;
margin: 0 1px;
background-color: #9E9EA1;
border-radius: 50%!important;
opacity: 0.4;
animation: bounce 0.7s linear infinite;
}
.typing-indicator span:nth-child(1)
{
animation-delay: 0.1s;
}
.typing-indicator span:nth-child(2)
{
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3)
{
animation-delay: 0.1s;
}
#keyframes bounce {
30% { transform: translateY(-4px); }
60% { transform: translateY(0px); }
80% { transform: translateY(4px); }
100% { transform: translateY(0px); opacity: 0.5; }
}
"dependencies": {
"botframework-webchat": "^4.14.0",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-scripts": "^3.4.1",
},
The problem was caused by the order in which I sent activities from the bot to webchat. I used to send:
typing-indicator event
message event
typing-indicator event
message event
The activity middleware would see the typing indicator event and replace the default render ('void') with a new rendering (my typing indicator).
My assumption is that the next event (the message) will be rendered without an avatar because webchat is already under the assumption that an avatar has been rendered.
To bypass this behavior my solution was to send
message event
typing-indicator event
message event
From a Ux perspective not such a bad idea and the problem does not occur

Having problem in Dynamic CSS in Vue-laravel

Here's the template where my button and contactList1 reside:-
<template>
<div class="chat-app">
<button v-on:click="displayList1()">Contacts List 1</button> //Button
<Conversation :contact="selectedContact" :messages="messages" #new="saveNewMessage" v-bind:class="{conversation:conversation}" />
<ContactsList :contacts="contacts" #selected="startConversationWith" v-bind:class="{contactsList1:contactsList1}"/> //contactsList
</div>
</template>
The object is default set to false
data() {
return {
contactsList1: {
default: false,
},
},
Method:-
displayList1()
{
this.contactsList1 = false;
},
Style:-
<style lang="scss" scoped>
.chat-app {
display: flex;
}
.contactsList1 {
background-color: black;
}
</style>
Even after the object being false the css is being applied, can anyone tell me what's wrong. I am just a beginner, Please help.
Your data function is returning the object contactsList1 and the full path to check the data type is this.contactsList1.default
You should also name your variables differently.
So here is a basic example on how to bind a Boolean datatype to your component class:
new Vue({
el: "#app",
data() {
return {
firstClass: {
status: false
}
}
},
methods: {
changeColour() {
this.firstClass.status = true
}
}
})
.styleFirstClass {
background: red
}
.itemBox {
padding:30px;
margin:30px;
border: 1px solid #444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="changeColour()">Click to bind class</button>
<div class="itemBox" :class="{styleFirstClass: firstClass.status}">
This is some text
</div>
</div>

Vue js applications code throws error (Js Expected)

I am trying to build vue js applications but I am getting following errors .
Severity Code Description Project File Line Suppression State
Warning TS1005 (JS) ':' expected. VuejsApp JavaScript Content Files C:\Users\Khundokar Nirjor\Documents\Visual Studio 2017\Projects\VuejsApp\VuejsApp\src\App.vue 19 Active
This is Home.vue code
<template>
<div id="databinding">
<div id="counter-event-example">
<p style="font-size:25px;">Language displayed : <b>{{ languageclicked }}</b></p>
<button-counter v-for="(item, index) in languages"
v-bind:item="item"
v-bind:index="index"
v-on:showlanguage="languagedisp"></button-counter>
</div>
</div>
</template>
<script>
// import Home from './components/Home.vue';
// import component1 from './components/component1.vue';
export default {
name: 'app',
Vue.components('button-counter', {
template: '<button v-on:click = "displayLanguage(item)"><span style = "font-size:25px;">{{ item }}</span></button>',
data: function () {
return {
counter: 0
}
},
props: ['item'],
methods: {
displayLanguage: function (lng) {
console.log(lng);
this.$emit('showlanguage', lng);
}
},
});
new Vue({
el: '#databinding',
data: {
languageclicked: "",
languages: ["Java", "PHP", "C++", "C", "Javascript", "C#", "Python", "HTML"]
},
methods: {
languagedisp: function (a) {
this.languageclicked = a;
}
}
})
};
</script>
<style>
</style>
I want to display list of buttons and when i clicked the any of them button , I want to display the message that button is clicked.
I believe your error is related to the component. First, the function name is wrong. The correct name is Vue.component and it is Vue.components. Second, your component declaration is not correct.
I created this codepen where you can see how to declare the component globally and locally.
const buttonCounter = {
template:
`<button #click="displayLanguage(item)">
<span style="font-size:25px;">{{ item }}</span>
</button>`,
props: ["item"],
methods: {
displayLanguage: function(lng) {
this.$emit("show-language", lng);
}
}
}
// Declare your component globally, will be able to access it in any another component in the application
Vue.component("button-counter", buttonCounter );
new Vue({
el: "#databinding",
// declarete your component locally, it only will be available inside this context
components:{
'button-counter-2' : buttonCounter
},
data: function() {
return {
languageclicked: "",
languages: ["Java", "PHP", "C++", "C", "Javascript", "C#", "Python", "HTML"]
};
},
methods: {
languageDisp: function(a) {
this.languageclicked = a;
}
}
});
body {
margin: 20px;
}
.btn-wrapper {
display: flex;
margin-bottom: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="databinding">
<div id="counter-event-example">
<p style="font-size:25px;">Language displayed : <b>{{ languageclicked }}</b></p>
<div class="btn-wrapper">
<button-counter v-for="(item, index) in languages" :item="item" :key="item" #show-language="languageDisp"/>
</div>
<button-counter-2 v-for="(item, index) in languages" :item="item" :key="item" #show-language="languageDisp"/>
</div>
</div>

Vue.Js | Components isn't working. | Unknown custom element: <image-upload>

I have this error :
[Vue warn]: Unknown custom element: <image-upload> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
In myApp.js i added :
Vue.component('image-upload',require('./components/ImageUpload.vue').default);
In ImageUpload.Vue i have this code:
<script>
import Croppie from 'croppie'
export default {
props: ['imgUrl'],
mounted() {
this.image = this.imgUrl
this.setupCroppie()
},
data() {
return {
croppie: null,
image: null
}
},
methods: {
setUpCroppie() {
let el = document.getElementById('croppie')
this.croppie = new Croppie(el, {
viewport: {width: 200, height: 200, type: 'circle'},
boundary: {width: 220, height: 220},
showZoomer: true,
enableOrientation: true
})
this.croppie.bind({
url: this.image
})
},
}
}
</script>
<template>
<div class="image-upload-wrapper image-upload">
<p>this is my component</p>
<div id="croppie"></div>
</div>
</template>
In blade:
<image-upload img-url="{{url('img/user.png')}}"></image-upload>
I'm following the doc here to use CroppieJs
https://foliotek.github.io/Croppie/
Thanks..
Have you tried to add a : to your vue component.
<image-upload :img-url="{{url('img/user.png')}}"></image-upload>
Does the Vue component appear in the Vue.js inspecter?
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd

Resources