Page doesn't update on pagination click in Laravel SPA - laravel

I am creating an SPA with Laravel and VUEJS. Everything works well:
- The routing changes accurately from /people to /people?page=2
- The data in the table displays correctly.
But, the problem is, if I click the pagination, the data inside the table doesn't update. The URL is updated /people?page=3 but records are still the same. Here are my codes below.
routes.js
export default[
{
path:'/people?page=:page',
component: ListPeople,
name: 'people.paginate',
meta:{
title: 'Paginate'
}
},
{
path: '/people',
component: ListPeople,
name: 'people.list',
meta: {
title: 'People List',
}
},
ListPeople.vue
I have listed here the table.
<template>
<div class="container">
<h1>People List</h1>
<vue-table v-bind="{data:people.data,columns}"></vue-table>
<vue-pagination :pagination="people"
#paginate="getPeople"
:offset="4">
</vue-pagination>
</div>
Paginate.vue
From my pagination, I have something like below:
<template>
<!-- more codes here -->
<router-link class="page-link" :to="{ name:'people.paginate', params:{ page: page }}">{{ page }}</router-link>
<!-- more codes here -->
</template>
<script>
export default{
props: {
pagination: {
type: Object,
required: true
},
offset: {
type: Number,
default: 4
}
},
computed: {
pagesNumber() {
if (!this.pagination.to) {
return [];
}
let from = this.pagination.current_page - this.offset;
if (from < 1) {
from = 1;
}
let to = from + (this.offset * 2);
if (to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
let pagesArray = [];
for (let page = from; page <= to; page++) {
pagesArray.push(page);
}
return pagesArray;
}
},
methods : {
changePage(page) {
this.pagination.current_page = page;
this.$emit('paginate',page);
}
}
}
</script>

You need to add a key to your vue-table component that changes when the pagination changes.
<vue-table v-bind="{data:people.data,columns}" :key="currentPage" ></vue-table>
So in your ListPeople component perhaps in your getPeople method update the key as you paginate.
data(){
return:{
currentPage: 1;
}
}
methods:{
getPeople(page){
//.. do something
this.currentPage = page;
}
}
If you include a key on a child component, when the parent template renders, Vue sets a getter/setter on the key and automatically adds it to the watcher instance of the component. So when the key changes it automatically rerenders the child component. Without the key the child component will remain in its cached state.

Related

vue not loading data into child component

I've a hard time in understanding the methods of vue. In my put-request users can edit, delete images. In parent component the get-request loads the images and the are pushed to an image-gallery (the child-component) via properties. In my set up the console.log is always empty.
//PARENT COMPONENT
<template>
<div class="form-group">
<image-gallery :serverData="serverMap"/>
</div>
</template>
<script>
import ImageGallery from './ImageGallery.vue';
export default {
components:{ImageGallery},
data: () => ({
serverMap: {
title: '',
file: ''
}
}),
mounted () {
//AJAX ETC get servermap
.then((response) => {
this.serverMap = response.data
})
}
Just a normal straight parent-child situation. Here under the child-component
<template>
</template>
<script>
export default {
name: 'ImageGallery',
//incoming data
props: {
serverData: {
type: Object,
default () {
return {
hasLabels: true,
isHorizontal: false
}
}
}
},
created: function () {
this.loadImages()
},
methods: {
loadImages () {
console.log(this.serverData.file)
//do something with the serverData
//prepare for fileReader function
//together with new image validation
}
}
The method 'loadImages' should be automatically delevering the serverData via computed.But is doesn t. Who can help?
There is race condition.
Either not render a child until data is available; serverMap needs to be null instead of empty object in order to be distinguished from populated object:
<image-gallery v-if="serverMap" :serverData="serverMap"/>
Or delay data access in a child until it's available instead of doing this immediately in created:
watch: {
serverData(data) {
if (data)
this.loadImages()
}
}

Vuex get data via slim (Ruby on rails)

How to transfer data if I receive an array via Slim?
regions-list :region=#regions
regions-list - my component vue
:region - array with items
#regions - variable with items from backend
Im new with vuex, i think, i need something like this, but don’t know how to convey array with items
This is how you can organize the work of Vuex
export default new Vuex.Store({
state: {
reactions: [],
},
mutations: {
setReactions(state, segment) {
state.reactions = segment;
},
},
actions: {
async loadReactions({ commit }) {
try {
const reactions = '... response/request';
commit('setReactions', reactions); // Here we use mutation to put new data into state.
} catch (e) {
// ...
}
},
},
});
In your component vue regions-list
<template>
<div>{{ reactions }}</div> <!-- Here you can display and look at the content -->
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'RegionsList',
computed: {
...mapState(['reactions']), // This is get the state
},
created() {
this.loadReactions(); // Here you perform a function that receives data and puts it in state
},
methods: {
...mapActions(['loadReactions']),
},
};
</script>
<style scoped></style>

Page load without refresh in svelte and dynamic routing

What is the best way to load a page without refreshing using graphql and dynamic routing.
I have a file called kindergarten that loads perfectly without refreshing the whole page :
<script context="module">
import { gql, GraphQLClient } from 'graphql-request'
export async function load() {
const graphcms = new GraphQLClient(import.meta.env.VITE_GRAPHCMS_URL, {
headers: {},
})
const query = gql`
query MyQuery {
terms(where: { taxonomies: CATEGORY }) {
nodes {
slug
name
termTaxonomyId
}
}
}
`
const { terms } = await graphcms.request(query)
return {
props: {
posts: terms.nodes,
},
}
}
</script>
<script>
import { SITE_NAME } from '$lib/store.js'
let date = new Date()
const [month, day, year] = [
date.getMonth() + 1,
date.getDate(),
date.getFullYear(),
]
export let posts = []
</script>
<svelte:head>
<title>Sample Title - {SITE_NAME}</title>
<meta
name="description"
content="Sample description [Update: {year}/{month}/{day}]" />
</svelte:head>
{#each posts as post (post.termTaxonomyId)}
<a
tax-id={post.termTaxonomyId}
href="/kindergarten/province/{post.slug}"
target="blank">
{post.name}
</a>
<br />
{/each}
and also I have another page called [slug].svelte :
<script context="module">
import { gql, GraphQLClient } from 'graphql-request'
export async function load(ctx) {
let slug = ctx.page.params.slug
const graphcms = new GraphQLClient(import.meta.env.VITE_GRAPHCMS_URL, {
headers: {},
})
const query = gql`
query MyQuery {
terms(where: { taxonomies: CATEGORY, slug: "${slug}" }) {
nodes {
name
description
}
}
}
`
const { terms } = await graphcms.request(query)
return { props: { slug, post: terms.nodes } }
}
</script>
<script>
import { SITE_NAME } from '$lib/store.js'
export let slug
export let post
</script>
<svelte:head>
<title>{post[0].name} - {SITE_NAME}</title>
</svelte:head>
<h1>Slug : {slug}</h1>
{#each post as data}
<p>Name: {data.name}</p>
<br />
{#if data.description}
<p>Description: {data.description}</p>
{:else}
<p>Ther is no Description</p>
{/if}
{/each}
When I click a link on kindergarten page it goes to the subpage but refreshes the whole site.
How can I optimize the [slug].svelte file to prevent refreshing the page?
As I'm new to Svelte and Sveltekit, any ideas for optimizing the whole code is appreciated.
You're linking to a new page, so it makes sense it refreshes, because it's going to a whole new page ([slug].svelte). It sounds like you're trying to load data into your kindergarten.svelte page? In that case, make a component, not a page, where you can pass in data to the component, and the component will be updated, rather than the entire page. Check out an example from the docs here: https://svelte.dev/tutorial/component-bindings

Refreshing data after-the-fact in AlpineJS

I'm using Alpine to display a list of items that will change. But I can't figure out how to tell Alpine to refresh the list of items once a new one comes back from the server:
<div x-data=" items() ">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
The first "batch" of items is fine, because they're hard-coded in the items() function:
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
]
};
}
Some code outside of Alpine fetches and receives a completely new list of items, that I want to display instead of the original set. I can't figure out how, or if it's even currently possible. Thanks for any pointer.
There are 3 ways to solve this.
Move the fetch into the Alpine.js context so that it can update this.items
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
],
updateItems() {
// something, likely using fetch('/your-data-url').then((res) => )
this.items = newItems;
}
};
}
(Not recommended) From your JavaScript code, access rootElement.__x.$data and set __x.$data.items = someValue
<script>
// some other script on the page
// using querySelector assumes there's only 1 Alpine component
document.querySelector('[x-data]').__x.$data.items = [];
</script>
Trigger an event from your JavaScript and listen to it from your Alpine.js component.
Update to the Alpine.js component, note x-on:items-load.window="items = $event.detail.items":
<div x-data=" items() " x-on:items-load.window="items = $event.detail.items">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
Code to trigger a custom event, you'll need to fill in the payload.
<script>
let event = new CustomEvent("items-load", {
detail: {
items: []
}
});
window.dispatchEvent(event);
</script>
Expanding on Hugo's great answer I've implemented a simple patch method that lets you update your app's state from the outside while keeping it reactive:
<div x-data="app()" x-on:patch.window="patch">
<h1 x-text="headline"></h1>
</div>
function app(){
window.model = {
headline: "some initial value",
patch(payloadOrEvent){
if(payloadOrEvent instanceof CustomEvent){
for(const key in payloadOrEvent.detail){
this[key] = payloadOrEvent.detail[key];
}
}else{
window.dispatchEvent(new CustomEvent("patch", {
detail: payloadOrEvent
}));
}
}
};
return window.model;
}
In your other, non-related script you can then call
window.model.patch({headline : 'a new value!'});
or, if you don't want assign alpine's data model to the window, you can simply fire the event, as in Hugo's answer above:
window.dispatchEvent(new CustomEvent("patch", {
detail: {headline : 'headline directly set by event!'}
}));

Guess login with firebase and ember

i try to implement a anonymous login with Ember and Firebase. I have successfully configure my project for use with Firebase and Emberfire, and i can login to my Firebase. But when i try to save user information in a Session initializer, i can't retrieve it to make my controllers aware of the user state.
This is my code :
I have a sidebar in my application.hbs that i want to display if the user is connected.
<div class="container-fluid" id="main">
<div class="row">
{{#if loggedIn}}
<aside class="col-xs-3">
{{outlet sidebar}}
</aside>
<div class="col-xs-9">
{{outlet}}
</div>
{{else}}
<div class="col-xs-12">
{{outlet}}
</div>
{{/if}}
</div>
</div>
Inside of my application.js controller i try to define a computed property :
import Ember from "ember";
var ApplicationController = Ember.ObjectController.extend({
loggedIn :function() {
console.log("Tota", this.session.get('isConnected'));
return this.session.get('isConnected');
}.property(this.session.get('isConnected')),
});
export default ApplicationController;
this.session references an Initializer that is inject inside of controllers and routes :
import Ember from 'ember';
export function initialize(container, application) {
var session = Ember.Object.extend({
authData : [],
user : null,
login : function(authData, user) {
console.log(authData);
console.log(user);
this.set('authData', authData);
this.set('user',user);
},
getUser: function() {
return this.get('user');
},
getAuthData: function() {
return this.get('authData');
},
isConnected : function() {
return (this.get('user') == null) ? false : true;
}.property('user')
});
application.register('session:main', session, { singleton: true });
// Add `session` object to route to check user
application.inject('route', 'session', 'session:main');
// Add `session` object to controller to visualize in templates
application.inject('controller', 'session', 'session:main');
}
export default {
name: 'session',
initialize: initialize
};
And this is my LoginController :
import Ember from "ember";
var LoginController = Ember.ObjectController.extend({
model : {},
ages : function() {
var ages = Ember.A();
for(var i = 18; i <= 99; i++) {
ages.push(i);
}
return ages;
}.property(),
sexs : ['Male', 'Female'],
actions : {
logIn : function() {
var data = this.getProperties("name", "age", "sex");
var that = this;
this.database.authAnonymously(function(error, authData) {
if (error) {
console.log(error);
} else {
var newUser = that.store.createRecord('user', {
name: data['name'],
age: data['age'],
sex:(data['age'] === "Male" ? 0 : 1)
});
newUser.save();
that.session.login(authData, newUser);
console.log("Toto", that.session.get('isConnected'));
that.transitionToRoute('chat');
}
});
}
}
});
export default LoginController;
So in my application.js, if i define loggedIn to be just a property() not property(this.session.get('isConnected'). loggedIn is not refreshed when the user connects to the application. If i tell it to computes with " this.session.get('isConnected') ", Ember tells me that "this.session" is not defined.
How to refresh this value, to tell to my template to display sidebar if my user is connected?
Simple answer
var ApplicationController = Ember.ObjectController.extend({
loggedIn :Em.computed.alias('session.isConnected'),
// or
loggedIn : function(){
return this.get('session.isConnected');
}.property('session.isConnected')
});
Your problem was your dependencies. Either you weren't watching it (property()) or you were crashing because this.session doesn't exist in the scope of the window. And I doubt Ember was really yelling it at you, more of just the javascript engine while it parsing your javascript.
loggedIn :function() {
console.log("Tota", this.session.get('isConnected'));
return this.session.get('isConnected');
}.property(),
// this is resolved while defining the controller, think of its scope
It is resolved like this:
var tmp = this.session.get('isConnected');
var tmp2 = function() {
console.log("Tota", this.session.get('isConnected'));
return this.session.get('isConnected');
}.property(tmp);
var tmp3 = {
loggedIn: tmp2
};
var ApplicationController = Ember.ObjectController.extend(tmp3);

Resources