I am playing with inertia.js and one thing I don't really understand is how to manage data across components.
For instance, I have a component that shows the number of visits to the page:
<template>
<div>{{visits}}</div>
</template>
<script setup>
defineProps({visits: Number})
</script>
On the backend I do have this:
Route::get('/visit', function () {
return inertia('page', { Visits::sum() });
});
However when I load my main page:
<template>
<h1>Main</h1>
<Visits/>
</template>
it would not fetch the visits data. From what I understood, with Inertia the parent component is responsible for loading all child data:
<template>
<h1>Main</h1>
<Visits :boilerplateData="visits"/>
</template>
<script setup>
defineProps({visits: Number, component1Data: Object, component2Data: Object ...})
</script>
Am I missing something?
Related
In the following example, the $ref pointing to the child's text does not persist when it is exposed using defineExpose(). Changes to the <input> are ignored. The parent can only access the initial value ("hello").
The expected behavior is seen when using the standard ref() instead of Reactivity Transform $ref()
What am I doing wrong here?
App.vue
<template>
<Child ref="child" />
<p>Child text: {{child?.text}}</p>
</template>
<script setup>
import Child from './Child.vue'
const child = $ref();
</script>
Child.vue
<template>
<input v-model="text" />
</template>
<script setup>
const text = $ref('hello');
defineExpose({ text })
</script>
Live example on Vue playground
Here's what I found by digging deeper in the Vue docs:
While reactive variables relieve us from having to use .value everywhere, it creates an issue of "reactivity loss" when we pass reactive variables across function boundaries.
To remedy this, they have $$(). Using it during defineExpose() solves my issue.
<template>
<input v-model="text" />
</template>
<script setup>
const text = $ref('hello');
defineExpose(
$$({ text })
)
</script>
Working example on Vue playground
I'll try to explain my problem.
I have a VueJS component, that uses slots in its template. For example:
App.vue:
<template>
<div>
<slot name="content" :content="somecontent"></slot>
</div>
</template>
<script>
export default {
data(){
return {
somecontent: "test"
}
}
}
</script>
And now I would like to initialize it like this:
<App>
<template #content="somecontent">
<div>{{somecontent}}</div>
</template>
</App>
The idea is that the user would be able to override a part of the widget content and still have some data that the widget provides. A real-life example would be a list where the elements are being loaded remotely and the user can override the list item template.
I know now that custom elements do not support scoped slots. Is there any other way to achieve this? The template syntax does not need to be like above.
Thanks for any info that could solve my problem.
I'm trying to append multiple vuejs components with jquery ajax, but it's not working.
It all works fine, until response returns more than one component, or component within component.
Here's the code:
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<div id="app">
</div>
<script type="text/x-template" id="template-1">
<div>
<h1>Template 1 {{param}}</h1>
</div>
</script>
<script type="text/x-template" id="template-2">
<div>
<h1>Template 2 {{param}}</h1>
</div>
</script>
<script>
Vue.component('template-1', {
template: '#template-1',
props: ['param']
});
Vue.component('template-2', {
template: '#template-2',
props: ['param']
});
const vm = new Vue({
el: '#app'
});
</script>
<script>
function loadMore(){
$.get('/home/test', function (response) {
var MyComponent = Vue.extend({
template: response
});
var compiled = new MyComponent().$mount();
$('#app').append(compiled.$el);
});
}
loadMore();
</script>
If the following response comes from ajax, it works, and renders one component:
<template-1 param="Test"></template-1>
If the following response returns from ajax, it renders only first component:
<template-1 param="Test"></template-1>
<template-2 param="Test"></template-2>
If the following response returns from ajax, it renders both components:
<div>
<template-1 param="Test"></template-1>
<template-2 param="Test"></template-2>
</div>
If the following response returns from ajax, it renders only parent component:
<template-1 param="Test">
<template-2 param="Test"></template-2>
</template-1>
Is there a way to make this work always, without knowing how many components will be returned from the server?
Since you are telling Vue that your response.data should be the value of the el property, it will look inside it and render all components that were registered and therefore known.
In your case you should always wrap your response inside another HTML element, which then should be the value of the el property.
Notice: Vue elements inside other Vue elements can only be rendered, if the parent component (here your template-1) has registered the second one itself. Means when template-1 got rendered, everything inside it will be removed, except it is using slots.
If you want to use slots, this could help you.
Otherwise you could use the render function and build your own dynamic render stuff.
In my Vue 2.6.X app I have single-file components such as:
<template>
<div>
<div v-if="users.length">
<!-- render the users in a list -->
</div>
<div v-else>
Warning: no users were found
</div>
</div>
</template>
<script>
import userService from '#/services/user-service'
export default {
name: 'UserList',
async created () {
this.users = userService.loadUsersFromServer()
},
data () {
return {
users: []
}
}
}
</script>
A problem with this approach is that after the component renders but before the users are loaded, the warning message is briefly shown in the UI. Is there a recommended way to avoid this "flash of invalid UI state"?
One approach is to introduce a loading data prop that is initialised to true, then set to false when the AJAX request has completed, and change the template to
<template>
<div>
<div v-if="loading">
Users are loading, please wait....
</div>
<div v-else-if="users.length">
<!-- render the users in a list -->
</div>
<div v-else>
Warning: no users were found
</div>
</div>
</template>
In this simple example, this should work fine, but if we need to wait on multiple requests before showing the UI, it could become cumbersome, is there a better/simpler approach?
Use Promise.all() to wait on multiple promises before setting loading to false.
That is simplest and what I do most of the times
Or you can defer navigation until the data is loaded if you use router
In my initial App.vue I'm dispatching an action that checks the authentication of a user. The component looks as follows:
<template>
<v-app>
<spinner v-if="!user" :status="spinner.status" :color="spinner.color" :size="spinner.size" :depth="spinner.depth" :rotation="spinner.rotation" :speed="spinner.speed"></spinner>
<div v-if="style">
<app-header></app-header>
<v-content style="margin-left:24px;margin-right:24px;">
<!-- <v-container grid-list-md text-cs-center> -->
<router-view v-if="user"></router-view>
<!-- </v-container> -->
</v-content>
<app-footer></app-footer>
</div>
</v-app>
</template>
In this component I add the component. This will render as soon as we know the auth status.
Once the auth status has been determined (logged in/out) I dispatch another action which is responsible for fetching styles from the server. The admin user gets a generic style while a normal user would get a styling based on a dynamic value in the URL.
Eg: yoursite.com/company_one would fetch styles from the server where the company is company_one
When I try to access the router in my store.js file I receive the following output in the console.
console.log(router.history) =>
I now want to drill down to get the agency parameter so I try:
If I
console.log(router.history.current);
Which gives me the following output in the console:
All of the sudden all properties are empty. If I drill further to get to the agency, the console outputs undefined
Can somebody explain why this happens?
If you're looking for the current params, you can try this.$route.params like described here: https://router.vuejs.org/guide/essentials/dynamic-matching.html