How to set value in component from ajax request to parameters during creation in Vue? - ajax

I am learning Vue and trying to set one value in component during its creation from one Ajax request.
Here is the structure of src folder:
src
assets
components
Management.vue
router
index.js
vuex
modules
caseSuiteList.js
index.js
actions.js
getters.js
App.vue
main.js
Management.vue:
<template>
</template>
<script>
import {mapState} from 'vuex'
import store from 'vuex'
export default {
name: "Management",
computed: mapState({
suite_list: state => state.caseSuiteList.suite_list
}),
created(){
store.dispatch('refresh')
}
}
</script>
caseSuiteList.js:
import axios from 'axios'
const state = {
suite_list : {}
}
const mutations = {
refreshSuiteList(state, payload) {
state.suite_list = payload
}
}
const actions = {
refresh (context) {
axios.get('http://127.0.0.1:8601/haha').then(function(res){
console.log(res.data);
context.commit('refreshSuiteList', res.data);
}
});
}
}
export default {
state,
mutations,
actions
}
How to dispatch action of caseSuiteList.js in created() of Management.vue to make this happen?

Within the vue instance (or any component within) you access the store with this.$store.

Try dispatching in the created hook of your component.
created() {
this.$store.dispatch('refresh');
}
More information about Vuex: https://vuex.vuejs.org/guide/actions.html

Related

use loader before matching any route for context global store?

In a regular React App I'd use Redux to manage the state, where I'd dispatch the initial data before matching any route in App, however, Redux is not advised in Remix, so I'm using useContext instead.
Is there a way to call loaders to fetch initial data (e.g. session, objects, etc.) before/without having to match any route and to then store that data in the context global store and then can be accessed by any component whithin the store? That way, the API will only be called during app initialization.
I'm at this moment calling the initial data in the loader of root.tsx, getting it with useLoaderData and then passing it as a prop to StoreProvider to dispatch it in the global state, however, I don't think this should be done like that way.
export let loader: LoaderFunction = async ({ request }) => {
let user = await getUser(request);
const products = await db.product.findMany();
return { user: user?.username, products };
};
function App() {
const data = useLoaderData<LoaderData>();
return (
<html lang="en">
...
<StoreProvider initData={data}>
<body>
...
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</StoreProvider>
</html>
);
}
export default App;
I think doing the data loading on the root route loader is the best way.
If you don't like that approach you could also fetch on entry.server and entry.client.
For example in entry.client you probably have something like this:
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
hydrate(<RemixBrowser />, document);
So you can change it to do the fetch before calling hydrate.
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
fetch(YOUR_API_ENDPOINT)
.then(response => response.json())
.then(data => {
hydrate(
<YourContextProvider value={data}>
<RemixBrowser />
</YourContextProvider>,
document
)
});
And in entry.server you can change the handleRequest function to something like this:
import { renderToString } from "react-dom/server";
import { RemixServer } from "remix";
import type { EntryContext } from "remix";
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
let response = await fetch(YOUR_API_ENDPOINT)
let data = await response.json()
let markup = renderToString(
<YourContextProvider value={data}>
<RemixServer context={remixContext} url={request.url} />
</YourContextProvider>
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
By doing it on entry.client and entry.server the fetch will only happen once and it will never be triggered again.
I still recommend you to do it inside the loader of the root so after an action it can be fetched again to keep the data updated.

Get data from Laravel(in api folder) to Vue (placed in another folder)

I have Laravel in api folder and Vue is in the root folder, and I try to pass data from Laravel to Vue Components.From what I find I must use axios for this but I didn't know how. I am looking for a solution for some hours now, but nothing worked. PS. I didn't do anything in blade till now. Any help, please !?
api/routes/api.php
Route::get('/content', 'ContentController#index');
ContentController
public function index() {
$customers = Customer::all();
return $customers;
}
Vue component
<template>
</template>
<script>
import axios from 'axios'
export default {
name: "Home"
};
</script>
Since you created your Vue app using the Vue CLI, running vue serve starts your application at a local URL, you need to have your Laravel API app running as well so you can request data from it using Axios in Vue components
cd api
php artisan serve
Then in your template, you should have something like this
<template>
<div></div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
databaseConfiguration: "",
errors: {}
};
},
name: "Home",
created: function() {
axios
.get("http://127.0.0.1:8000/api/content")
.then(response => {
this.databaseConfiguration = response.data;
console.log(response.data);
})
.catch(error => {
this.errors.push(error);
console.log(error);
});
}
};
</script>
Here's a full working example app on GitHub
Hope this helps

Vue.js router view no components?

I am trying to make a vue SPA using vuex, vue-router & laravel for backend. I was separating our data on our app.js to try to reduce clutter and keep our code neat. When everything on one page it works as intended, loading the routes in the router. But when we separate the code to make it more modular into: app.js, boostrap.js, routes.js, and store.js
The components aren't loading in our router-view and we are able to see our RouterLink
app.js
// Require the bootstrapper
require('./bootstrap');
// Grab imports
import Store from './store';
import Router from './routes';
// Views
import App from './views/App';
// Create the application
const app = new Vue({
el: '#heroic',
components: { App },
store: Store,
router: Router
});
boostrap.js
// Imports
import Vue from 'vue';
import Axios from 'axios';
import Swal from 'sweetalert2';
// Add to window
window.Vue = Vue;
window.Axios = Axios;
// Add Axios headers
window.Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.Axios.defaults.headers.common['Authorization'] = 'Bearer ' + 'token';
window.Axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
routes.js
// Imports
import Vue from 'vue';
import VueRouter from 'vue-router';
import Store from './store';
// Set to use
Vue.use(VueRouter);
// Views
import Hello from './views/Hello';
import Home from './views/Home/';
import UserIndex from './views/UserIndex';
// Create our routes
const routes = [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/hello',
name: 'hello',
component: Hello,
},
{
path: '/users',
name: 'users.index',
component: UserIndex,
}
];
// Create the router
const router = new VueRouter({
mode: 'history',
routes: routes,
scrollBehavior (to, from, saved) {
if (saved) {
return saved;
}
return { x: 0, y: 0};
}
});
// Before every request
router.beforeEach((to, from, next) => {
});
// After every request
router.afterEach((to, from, next) => {
});
// Export
export default router;
hello.vue
<template>
<div class="row row-cards row-deck">
<div class="col-lg-4 col-md-6">
<p>Hello World!</p>
</div>
</div>
</template>
store.js
// Imports
import Vue from 'vue';
import Vuex from 'vuex';
import PersistedState from 'vuex-persistedstate';
import Cookie from 'js-cookie';
// Set use
Vue.use(Vuex);
// Create our store
const store = new Vuex.Store({
state: {
auth: [{
id: 1,
username: '',
motto: '',
rank: 1,
permissions: [],
token: ''
}],
users: [],
},
mutations:{
},
actions: {
},
getters: {
}
});
// Export
export default store;
The expected result is that when I visit the "/hello" route it would show the information that says "Hello world!" that is within the Vue file specified as the component in the routes section of the router. Instead using my Vue DevTools I get the following with no Hello world on the page.
https://i.pathetic.site/chrome_99Mbxf7f0c.png
My guess is the router is stuck waiting for the beforeEach (and also possibly afterEach) hook to be resolved. You need to call next().
Also unrelated, but if you’re using modules then you shouldn’t need to assign stuff on window.

Laravel & Vuejs mixins : how to attribute a new value to shared variable?

I can use the mixin variables test and the method changeTest, when when I attribute a new value to the variable test, it's only applied in one component. How to have it changed globally, on all components using it ?
My mixins are set in the file resources/js/mixins.js:
export default {
data() {
return {
test: 'foo',
};
},
methods: {
changeTest(v) {
this.test = v;
}
}
}
Then, I have my two components comp1.vue and comp2.vue in resources/js/components/, both looking like this:
<template>
<div>
{{ test }}
</div>
</template>
<script>
import myMixins from '../mixins'
export default {
mixins: [ myMixins ],
}
</script>
Both components are in my home.blade.php like this:
#extends('layouts.app')
#section('content')
<comp1></comp1>
<comp2></comp2>
#ensection
for making a common variable (state) between all instances in vue.js you can use vuex. it's so simple, just add vuex to your packages and make an instance like this:
import Vuex from 'vuex'
const store = new Vuex.Store({
state: {
test: 'foo',
},
mutations: {
setTest(state, payload) {
state.test = payload
}
},
});
now you need to add this store to your main vue instance:
import Vue from 'vue'
Vue.use(Vuex);
let vm = new Vue({
el: '#app',
store,
// ...
});
all done. now in every component you can access the states by this.$store.state. for making life easier, you can define a computed variable like this:
computed: {
test() {
return this.$store.state.test
}
}
to change the state you just need to commit the setTest mutation. you have to add this mutation to methods and simply call it like this:
methods: {
...Vuex.mapMutations(['setTest']),
myMethod() {
// do this
this.setTest('some value');
// do that
}
}
you can also make a global mixin like i told you before to add this computed and mutation to every instance like this: (add this before make the main vue instance)
Vue.mixin({
computed: {
test() {
return this.$store.state.test
}
},
methods: {
...Vuex.mapMutations(['setTest']),
}
});
but i don't recommend to do this because when the project grow big, it gets so hard to avoid name confusion. it's better to make them separately for each component to chose proper names.
you can use mixin method on main Vue instance like this:
import Vue from 'vue'
import MyMixin from './mixins.js'
Vue.mixin(MyMixin);
It will apply this mixin for all instance no matter how deep they are.

Vuejs setting up event bus

So in my root app.js i have
window.Vue = require('vue');
const EventBus = new Vue()
Object.defineProperties(Vue.prototype, {
$bus: {
get: function () {
return EventBus
}
}
})
const app = new Vue({
el: '#backend',
EventBus,
components: {
FirstComponent
}
});
Now in the first component
clickbtn(){
this.$bus.$emit('test', { "testval":"setting up event bus" })
}
components:{
ChildComponent //local component
}
Now on the child component
created(){
this.$bus.$on('test', ($event) => {
console.log('Test event triggered', $event)
})
}
Where am i going wrong in the setup since even console.log(this) doesnt have $bus in it.
I was following This to setup
I still would like to use $bus as it looks good and abit organized.How do i make it happen.
I usually do a separation with the EventBus.
eventbus.js
import Vue from 'vue';
export const EventBus = new Vue();
Then i simply do an import in every component that needs to listen for event. On bigger projects I would even create a events.js and eventListener.js file and then handle everything there.
With complete separation
eventbus.js
This will be our event bus and is used from all other places.
import Vue from 'vue';
export const EventBus = new Vue();
event.js
This file is basically a library of common events. This makes it easier to maintain.
import { EventBus } from './Eventbus.js';
import { Store } from './Store.js'; // If needed
class Event {
// Simple event
static showMessage(message) {
EventBus.$emit('showMessage', message);
}
}
eventlistener.js
Event listener for our common events. Again this makes it easier to maintain. This could be in the same event file, but I like the separation.
import { EventBus } from './Eventbus.js';
class EventListener {
// Simple event listener
static showMessage() {
EventBus.$on('showMessage', function() {
alert(message);
});
}
// Simple event listener with callback
static showMessage(callbackFunction) {
EventBus.$on('showMessage', callbackFunction);
}
}
ComponentA.vue
A random component. Imports the EventBus and Event collection as it is used somewhere in the vue component.
<template>
*SOME HTML*
</template>
<script>
import { Event } from 'event.js'
import { EventBus } from 'eventbus.js';
export default {
methods: {
throwAlert: function() {
Event.showMessage('This is my alert message');
}
}
}
</script>
ComponentB.vue
A random component. Imports the EventBus and EventListener collection as it is suppose to react on events on the eventbus.
<template>
*SOME HTML*
</template>
<script>
import { EventListener } from 'eventlistener.js'
import { EventBus } from 'eventbus.js';
export default {
mounted() {
// Will start listen for the event 'showMessage' and fire attached function defined in eventlistener.js
EventListener.showMessage();
// Will start listen for the event 'showMessage' and execute the function given as the 'callbackFunction' parameter. This will allow you to react on the same event with different logic in various vue files.
EventListener.showMessage(function(message) {
alert(message);
});
}
}
</script>

Resources