How to load Vue directive with Inertia JS in Laravel (Jetstream) - laravel

My original app.js looked like this. I noticed I can't add Vue directives because the createApp has no variable.
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.mixin({ methods: { route } })
.mount(el);
},
});
After some research this lead me to this where the directive still doesn't work. Any tips?
const clickOutside = {
beforeMount: (el, binding) => {
el.clickOutsideEvent = event => {
// here I check that click was outside the el and his children
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted: el => {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
const el = document.getElementById('app');
const app = createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
});
app.directive('click-outside', clickOutside);
app.mixin({ methods: { route } })
.mixin({ directives: { clickOutside } })
.use(InertiaPlugin);
app.mount(el);

Related

Default layout doesn't work in Laravel + Vite + Svelte

I started my first project with Laravel + Vite (I already used Inertia with Laravel + Webpack) and the problem I have is the default layout.
When using Webpack I could define the layout with the following code:
createInertiaApp({
resolve: name => {
const page = require(`../svelte/Pages/${name}.svelte`);
if (guestPages.indexOf(name) !== -1) {
page.layout = LayoutGuest
} else {
page.layout = Layout
}
return page
},
setup({ el, App, props }) {
new App({ target: el, props })
},
})
But now, with the new Vite way, I can't get it to work.
Here's the code I have:
async function resolve(name)
{
const page = resolvePageComponent(`../svelte/Pages/${name}.svelte`, import.meta.glob('../svelte/Pages/**/*.svelte'));
let component;
await page
.then(module => {
module.default.layout = Layout;
component = module;
});
return component;
I don't know if the problem is the dynamic import.
With the help of an Inertia server member on Discord (Robert Boes) and writing a few lines of code, I found the solution:
// import './bootstrap';
import { createInertiaApp } from '#inertiajs/inertia-svelte'
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import "../less/app.less";
import Layout from "../svelte/Base/Layout.svelte";
async function resolve(name)
{
let component;
const pagesWithoutLayout = [
'Session/Index',
];
const page = resolvePageComponent(`../svelte/Pages/${name}.svelte`, import.meta.glob('../svelte/Pages/**/*.svelte'));
await page
.then(module => {
component = pagesWithoutLayout.includes(name) ?
module :
Object.assign({ layout: Layout }, module);
});
return component;
}
createInertiaApp({
resolve,
setup({ el, App, props }) {
new App({ target: el, props })
},
});
I made it work without Svelte with the following code snipped.
Hope it helps and maybe you can adapt it to your needs.
Im using it in a TypeScript environment, which is why I added "as any;".
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: async (name) => {
const page = resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob("./Pages/**/*.vue")
) as any;
await page.then((module) => {
//if name starts with auth, then use login layout
if (name.startsWith("Auth/")) {
module.default.layout = module.default.layout || LoginLayout;
} else if (name.startsWith("Public/")) {
module.default.layout = module.default.layout;
} else {
module.default.layout = module.default.layout || AppLayout;
}
});
return page;
},
setup({ el, app, props, plugin }) {
createApp({ render: () => h(app, props) })
.use(plugin)
.use(createPinia())
.mount(el);
},
});

vue-moment issue in laravel-inertia

I tried importing vue-moment and initializing it by using .use(VueMoment) as shown below. But after i do that the whole app shows error. Anyone facing the same problem?
require('./bootstrap');
// Import modules...
import { createApp, h } from 'vue';
import { App as InertiaApp, plugin as InertiaPlugin } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import VueMoment from 'vue-moment' ////////imported vue-moment
const el = document.getElementById('app');
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({
methods: {
route,
validationError(field){
if(this.$page.props.errors && this.$page.props.errors[field]){
return this.$page.props.errors[field];
}
else{
return null;
}
}
} })
.use(InertiaPlugin)
.use(VueMoment) /////use vue-moment
.mount(el);
InertiaProgress.init({ color: '#4B5563' });
This is the error i am getting
first install moment by
npm install moment
<template>
{{today}}
</template>
<script>
import moment from 'moment'
export default {
name:'Home',
data(){
return{
today:moment().startOf('day').toDate(), moment().endOf('day').toDate()
}
}
</script>
there is a working example of how to import 'vue-moment' from my Laravel + inertia project
const vm = new Vue({
metaInfo: {
titleTemplate: title => (title ? `${title} - Ping CRM` : 'Ping CRM'),
},
store,
render: h =>
h(App, {
props: {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => import(`#/Pages/${name}`).then(module => module.default),
},
}),
}).$mount(el)
Vue.use(require('vue-moment'))
Might help someone who is using InertiaJs with Vue and want to declare globally.
In app.js
createInertiaApp({
id: 'app',
setup({ el, App, props}) {
let app = createApp({
render: () => {
return h(App, props);
},
});
//you can declare any other variable you want like this
app.config.globalProperties.$moment = moment;
app.use(
store,
.....
).mount(el);
},
});
Now in the vue file you can call moment by
this.$moment(this.time,'H:m').format('hh:mm a');

Problem with calling action method through dispatch with webext-redux in browser extension

I'm trying to call apiAction in constructor method through the dispatch redux method in ReactJS Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './styles.scss'
import { fetchData, testSet } from '../../../../../event/src/cg-store/actions';
class AppDetails extends Component {
constructor(props) {
super(props);
this.state ={
testowaZmienna: ''
}
this.props.fetchData('5576900');
}
componentDidMount() {
document.addEventListener('click', () => {
this.props.addCount()
});
this.props.testSet()
this.props.fetchData('5576900');
console.log('dhsadhnaskjndaslndsadl-----------------------------------------')
}
render() {
const { error, test, count, testSetData, data } = this.props;
return (
<div>
TEST--------------------------
Count: {count}
Error: {error}
Test: {test}
TestSet: {testSetData}
Fetch: {data[0]}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
test: state.cg.test,
data: state.cg.data,
error: state.cg.error,
testSetData: state.cg.testSet,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (offerId) => dispatch(fetchData(offerId)),
addCount: () => dispatch({
type: 'ADD_COUNT'
}),
testSet: () => dispatch(testSet()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AppDetails);
As you can see there is addCount, testSet and fetchData methods. addCount and testSet works but problem is with fetchData:
This is apiAction method:
const fetchProductsPending = () => {
return {
type: actionTypes.FETCH_DATA_PENDING
};
};
const fetchProductsSuccess = fetchedData => {
return {
type: actionTypes.FETCH_DATA_SUCCESS,
data: fetchedData
};
};
const fetchProductsError = errorMessage => {
return {
type: actionTypes.FETCH_DATA_ERROR,
error: errorMessage
};
};
export const testSet = () => {
return {
type: actionTypes.TEST_SET
};
};
export const fetchData = (offerId) => (dispatch) => {
console.log('Im inside fetch before set pending'); // It does not want to go here
dispatch(fetchProductsPending());
axios
.get(config.api.host + offerId, {
headers: {
"Content-Type": "application/json",
}
})
.then(response => {
return response.data;
})
.then(response => {
dispatch(fetchProductsSuccess(response.data));
console.log("Fetch data success: ----------------------");
console.log(response.data);
})
.catch(error => {
dispatch(fetchProductsError(error.statusText));
console.log("Fetch data success: ----------------------");
console.log(error);
});
};
So as you can see testSet works fine but fetchData does not want to work.
What I'm doing wrong?

sinon fake server.requests returns an empty array

I'm not sure if I'm doing it correctly, I just followed their documentation.
I'm using mocha + sinonjs
actions.js
import axios from 'axios';
export const attachmentActions = {
getAttachment(id) {
axios.get('/api/attachment/${id}')
.then(response => {
console.log(respons);
})
.catch(error => {
console.log(error);
})
}
}
export const actions = {
...attachmentActions,
}
actions.test.js
describe('actions-test', () => {
let server;
beforeEach(() => {
server = sinon.createFakeServer();
});
afterEach(() => {
server.restore();
})
it('call getAttachment', () => {
server.requests[0].respond(200,
{'Content-type':'application/json'},
JSON.stringify({mimeType: 'image/png', path: '/user/image/'}));
actions.getAttachment(1)
//assertion
})
})
With the code above I got TypeError: Cannot read property 'respond' of undefined
I also tried below
it('call getAttachment', () => {
server.respondWith('GET','/api/attachment/1',[200,
{'Content-type':'application/json'},
JSON.stringify({mimeType: 'image/png', path: '/user/image/'}))];
actions.getAttachment(1);
server.respond();
//assertion
})
but when I run the test axios returns 404
Would really appreciate if someone can pinpoint if I miss something?
sinon version: 7.3.2

Can't invoke the component's method

Using an EventBus on my Laravel-Vuejs project. I'm emitting an items-updated event from ItemCreate component after the successful Item creation. On the same page I'm using ItemList component which shows a list of created Items
Here is the codes:
app.js file
require('./bootstrap');
window.Vue = require('vue');
window.EventBus = new Vue();
Vue.component('item-list',
require('./components/entities/item/ItemList'));
Vue.component('item-create',
require('./components/entities/item/ItemCreate'));
const app = new Vue({
el: '#app'
});
ItemCreate.vue Component
export default {
data: function () {
return {
itemName: ''
}
},
methods: {sendItemData: function () {
axios.post('/dashboard/item', {
name: this.itemName
})
.then(response => {
if (response.status === 201) {
toastr.success('Item created successfully!', {timeout: 2000});
EventBus.$emit('items-updated');
}
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
}
}
ItemList.vue Component
export default {
data: function () {
return {
items: [],
}
},
methods: {
getItems: function () {
axios.get('/dashboard/items')
.then(response => {
this.items = response.data;
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
},
mounted() {
this.getItems();
EventBus.$on('items-updated', function () {
this.getItems();
});
}
}
It was a general JS mistake. Working code:
on ItemList.vue Component
export default {
data: function () {
return {
items: [],
}
},
methods: {
getItems: function () {
axios.get('/dashboard/items')
.then(response => {
this.items = response.data;
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
},
mounted() {
this.getItems();
let vm = this;
EventBus.$on('items-updated', function () {
vm.getItems();
});
}
}

Resources