Has anyone used AntDesign ever encountered the problem with Ant's pagination component? I call api with limit of 7 and total is 15 records, antD should return all 3 pages but it only calculates and outputs 2 pages? It doesn't seem to use the Math.celi function for calculations
Here are some related code snippets:
In View:
<!-- Pagination -->
<span class="flex col-span-4 mt-2 sm:mt-auto sm:justify-end">
<nav aria-label="Table navigation">
<ul class="inline-flex items-center">
<a-pagination
v-if="!loading"
#change="pagination"
v-model:current="current"
:total="totalPage"
show-less-items
/>
</ul>
</nav>
</span>
In Script:
<script setup lang="ts">
import Pagination from "#/components/Pagination.vue";
const totalPage = ref<number>();
const current = ref(1);
async function getFile(page = 1) {
try {
const fileData = await request.get("api/admin/file/list", {
params: {
page,
limit: 7,
name: searchName.value.trim(),
status: stateStatus.value,
mimetype: typeFile.value,
lte_size: endSize.value,
gte_size: startSize.value,
},
});
files.value = fileData.data.data;
totalPage.value = fileData.data.count;
console.log("Total Page:",totalPage.value) //Log the correct results
loading.value = false;
} catch (e) {
console.log(e);
}
}
onMounted(async () => {
await getFile();
});
//Tính phân trang
async function pagination(pageNumber: number) {
current.value = pageNumber;
files.value = [];
await getFile(pageNumber);
}
</script>
Hope to get help from everyone, thanks a lot
Related
I've got a problem with my hobby project, I created a BlogPost.vue in which I render the previously clicked post from a Blog.vue page.
In this BlogPost, I render the clicked post's title, content etc, and on the sidebar, I show a small area in which I render 3 of the latest posts from this same category - the posts from Blog.vue.
When I click on the sidebar's links, any of the 3, the browser does change the slug, and the page does re-render itself, except it re-renders the same post that was clicked. If I refresh the page in the browser (or Ctrl+R or F5 etc), then it does render the correct content clicked from this sidebar.
I have no clue why it does that, I can only suppose that I should be watching the route change then somehow refresh what it renders, but no idea as to how.
Blog.vue, this works great, renders the single post clicked
<script setup lang="ts">
import axios from "axios";
import { ref } from "vue";
import { onMounted } from "vue";
import moment from "moment";
const postsUrl = "http://localhost/wordpress/wp-json/wp/v2/posts";
const posts = ref([] as any);
const isLoading = ref(false);
const errorCaught = ref(false);
var queryOptions = {
_embed: true,
};
const getPosts = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions })
.then((response) => {
posts.value = response.data;
console.log(posts.value);
isLoading.value = false;
})
.then(() => {
console.log(isLoading.value);
})
.catch((error) => {
if (error) {
isLoading.value = false;
errorCaught.value = true;
}
});
};
onMounted(async () => {
getPosts();
});
</script>
<template>
<transition name="fadeLoading">
<div v-if="isLoading" class="posts-loading">
<div class="circle"></div>
</div>
</transition>
<transition name="fadeLoading">
<div class="errorCaught" v-if="errorCaught">
There was an error loading news
</div>
</transition>
<div class="blog-container">
<div class="wrapper">
<transition-group name="fadeBlog">
<ul v-if="!isLoading" class="blog-posts-ul" v-for="post in posts">
<div class="posts-card">
<a
><router-link
:to="/blog/ + post.slug"
key="post.id"
class="posts-permalink"
>
</router-link
></a>
<img
v-if="post.featured_media != 0"
class="posts-featuredimage"
:src="post._embedded['wp:featuredmedia'][0].source_url"
:alt="post.title.rendered"
/>
<img v-else src="#/assets/logos/favicon-big.png" />
<div class="posts-date">
<p>
{{ moment(post.date).fromNow() + " " + "ago" }}
</p>
</div>
<div class="posts-text">
<h1 class="posts-title">{{ post.title.rendered }}</h1>
<p v-html="post.excerpt.rendered" class="posts-excerpt"></p>
</div>
</div>
</ul>
</transition-group>
</div>
</div>
</template>
BlogPost.vue, renders the previously clicked one, but does not show the sidebar's clicked content
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";
import moment from "moment";
const route = useRoute();
const postsUrl = "http://localhost/wordpress/wp-json/wp/v2/posts";
const queryOptions = {
slug: route.params.blogSlug,
_embed: true,
};
const post = ref([] as any);
const isLoading = ref(false);
const latestPostsAPI = "http://localhost/wordpress/wp-json/wp/v2/posts";
const latestPosts = ref([] as any);
const errorCaughtLatest = ref(false);
var queryOptionsLatest = {
_embed: true,
per_page:3,
};
const getLatest = () => {
axios
.get(latestPostsAPI, { params: queryOptionsLatest })
.then((response) => {
latestPosts.value = response.data;
console.log(latestPosts.value);
})
.catch((error) => {
if (error) {
errorCaughtLatest.value = true;
}
});
};
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions })
.then((response) => {
post.value = response.data;
console.log("Pages retrieved!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
getLatest();
getPost();
</script>
<template>
<div v-if="!isLoading" class="post-wrapper">
<div class="wrapper">
<div class="post-title">{{ post[0].title.rendered }}</div>
<div class="post-date">
{{ moment(post[0].date).format("MMMM Do YYYY, h:mm, dddd") }}
</div>
<!-- THIS INCLUDES HTML TAGS -->
<div class="post-content" v-html="post[0].content.rendered"></div>
</div>
</div>
<div class="side-container">
<div class="side-wrapper">
<ul v-if="!isLoading" class="blog-posts-ul" v-for="latest in latestPosts">
<div class="posts-card">
<a
><router-link
:to="/blog/ + latest.slug"
key="latest.id"
class="posts-permalink"
>
</router-link
></a>
<img
v-if="latest.featured_media != 0"
class="posts-featuredimage"
:src="latest._embedded['wp:featuredmedia'][0].source_url"
:alt="latest.title.rendered"
/>
<img v-else src="#/assets/logos/favicon-big.png" />
<div class="posts-text">
<div class="posts-title">{{ latest.title.rendered }}</div>
<div class="posts-date">
<p>{{ moment(latest.date).fromNow() + " " + "ago" }}</p>
</div>
<div class="posts-author">
{{ latest._embedded.author[0].name }}
</div>
</div>
</div>
</ul>
</div>
</div>
</template>
Thanks for your time
In my original post as you can see, I'm using axios to get my post with the params queryOptions:
I declare my queryOptions with a const:
const queryOptions = {
slug: route.params.blogSlug,
_embed: true,
};
THEN, I call the getPost method, which uses this constant:
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions } })
.then((response) => {
post.value = response.data;
console.log("getPost!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
And the problem is right there: that queryOptions, IS a constant, gets defined at the onMounted lifecycle, or even before I'm not 100% sure, but it does NOT get re-defined WHEN I click on any link on this BlogPost.vue component.
SO, the solving is the following:
Change the getPost method so it does NOT use a pre-defined constant, rather just simply type in the params, like the following:
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: { slug: route.params.blogSlug, _embed: true } })
.then((response) => {
post.value = response.data;
console.log("getPost!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
If I do it this way, every time the getPost method runs, which it WILL since I'm watching the blogSlug change with
watch(() => route.params.blogSlug, getPost);
the getPost function will always call the latest route.params.blogSlug, which gets changed before the watcher runs the getPost as I click on the sidebar link.
I have a nuxt.js project: https://github.com/AzizxonZufarov/newsnuxt2
I need to update posts from API every minute without loading the page:
https://github.com/AzizxonZufarov/newsnuxt2/blob/main/pages/index.vue
How can I do that?
Please help to end the code, I have already written some code for this functionality.
Also I have this button for Force updating. It doesn't work too. It adds posts to previous posts. It is not what I want I need to force update posts when I click it.
This is what I have so far
<template>
<div>
<button class="btn" #click="refresh">Force update</button>
<div class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s">
<StoryCard :story="s" />
</div>
</div>
</div>
</template>
<script>
definePageMeta({
layout: 'stories',
})
export default {
data() {
return {
err: '',
stories: [],
}
},
mounted() {
this.reNew()
},
created() {
/* setInterval(() => {
alert()
stories = []
this.reNew()
}, 60000) */
},
methods: {
refresh() {
stories = []
this.reNew()
},
async reNew() {
await $fetch(
'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'
).then((response) => {
const results = response.slice(0, 10)
results.forEach((id) => {
$fetch(
'https://hacker-news.firebaseio.com/v0/item/' +
id +
'.json?print=pretty'
)
.then((response) => {
this.stories.push(response)
})
.catch((err) => {
this.err = err
})
})
})
},
},
}
</script>
<style scoped>
.router-link-exact-active {
color: #12b488;
}
</style>
This is how you efficiently use Nuxt3 with the useLazyAsyncData hook and a setInterval of 60s to fetch the data periodically. On top of using async/await rather than .then.
The refreshData function is also a manual refresh of the data if you need to fetch it again.
We're using useIntervalFn, so please do not forget to install #vueuse/core.
<template>
<div>
<button class="btn" #click="refreshData">Fetch the data manually</button>
<p v-if="error">An error happened: {{ error }}</p>
<div v-else-if="stories" class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s.id">
<p>{{ s.id }}: {{ s.title }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { useIntervalFn } from '#vueuse/core' // VueUse helper, install it
const stories = ref(null)
const { pending, data: fetchedStories, error, refresh } = useLazyAsyncData('topstories', () => $fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'))
useIntervalFn(() => {
console.log('refreshing the data again')
refresh() // will call the 'topstories' endpoint, just above
}, 60000) // every 60 000 milliseconds
const responseSubset = computed(() => {
return fetchedStories.value?.slice(0, 10) // optional chaining important here
})
watch(responseSubset, async (newV) => {
if (newV.length) { // not mandatory but in case responseSubset goes null again
stories.value = await Promise.all(responseSubset.value.map(async (id) => await $fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)))
}
})
function refreshData() { refreshNuxtData('topstories') }
</script>
In a recent web app we have a lot of forms with the same submit structure:
Disable the form and submit button based on an isSubmitting variable
Validate the input fields (we're using Yup)
If validation fails: Set isSubmitting back to false + set and show validationErrors on the input fields
If validation succeed: Send post request with form data to api
Show general error if api is down or returns an error
I've tried to something using the composition api in vue 3.
Login.vue
<template>
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<h1 class="text-3xl text-center text-gray-900">{{ t('sign_in_account', 1) }}</h1>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<form #submit.prevent="handleSubmit">
<fieldset :disabled="isSubmitting" class="space-y-6">
<MessageBox v-if="errors.general" :title="errors.general" :messages="errors.messages" />
<Input :label="t('email', 1)" type="text" id="email" v-model="user.email" :error="errors.email" />
<Password :label="t('password', 1)" type="password" id="password" v-model="user.password" :error="errors.password" />
<div class="text-sm text-right">
<router-link class="font-medium text-indigo-600 hover:text-indigo-500" :to="forgotPassword">{{ t('forgot_password', 1) }}</router-link>
</div>
<SubmitButton class="w-full" :label="t('sign_in', 1)" :submittingLabel="t('sign_in_loader', 1)" :isSubmitting="isSubmitting" />
</fieldset>
</form>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import useForm from '#/use/useForm';
import { validateEmail, LoginValidationSchema } from '#/utils/validators';
export default {
setup() {
const store = useStore();
const { t } = useI18n({ useScope: 'global' });
const user = ref({
email: '',
password: '',
});
const { handleSubmit, isSubmitting, errors } = useForm(user, LoginValidationSchema, handleLogin);
async function handleLogin(values) {
try {
return await store.dispatch('auth/login', values);
} catch (error) {
if (error.response) {
console.log(error.reponse);
if (error.response.status == 422) {
errors.value = {
general: `${t('unable_to_login', 1)}<br /> ${t('fix_and_retry', 1)}`,
messages: Object.values(error.response.data.errors).flat(),
};
} else if (error.response.data.message) {
errors.value = {
general: error.response.data.message,
};
} else {
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
}
} else if (error.request) {
console.log(error.request);
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
} else {
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
}
return;
}
}
return { t, user, handleSubmit, isSubmitting, errors };
},
computed: {
forgotPassword() {
return validateEmail(this.user.email) ? { name: 'forgotPassword', query: { email: this.user.email } } : { name: 'forgotPassword' };
},
},
};
</script>
useForm.js
import { ref, watch } from 'vue';
export default function useForm(initialValues, validationSchema, callback) {
let values = ref(initialValues);
let isSubmitting = ref(false);
let errors = ref({});
async function handleSubmit() {
try {
errors.value = {};
await validationSchema.validate(values.value, { abortEarly: false });
isSubmitting.value = true;
} catch (err) {
console.log('In the catch');
isSubmitting.value = false;
err.inner.forEach((error) => {
errors.value = { ...errors.value, [error.path]: error.message };
});
}
}
watch(isSubmitting, () => {
if (Object.keys(errors.value).length === 0 && isSubmitting.value) {
callback(values);
isSubmitting.value = false;
} else {
isSubmitting.value = false;
}
});
return { handleSubmit, isSubmitting, errors };
}
This is somehow working but I'm missing two things. In useForm I want to wait till the callback is done (succeed or failed) to set isSubmitting back to false. Is a promise a good way to do this of is there a better way? Secondly I want a reusable way to handle the errors in Login.vue. Any suggestion how to handle this?
Regarding your first question - try..catch statements have a third statement called finally which always executes after the try statement block has completed.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
To answer your second question - promises are a great way of handling async logic, including your case when the API you're sending the request to returns an error response, and you can then decide how you're going to handle the UX in such scenario.
I'm not quite clear on what you mean by handle the errors in Login.vue in a reusable way, but perhaps you could simple pass in an empty array prop to your useForm called formErrors and have your useForm.js emit an update:modelValue event to get two way binding.
I just started learning Vue.js. I need to call the function in another component in my project. When I add new data to the table with createAnnouncement.vue, I want to go into announcement.vue and call the queryAnnouncement function. How can I do that? I would appreciate if you could help, please explain with a sample. Or edit my codes.
Announcement.Vue template:
<template>
// more div or not important template code
<div class="dataTables_filter" style="margin-bottom:10px">
<label>
<input class="form-control form-control-sm" placeholder="Search" aria-controls="m_table_1" type="search" v-model="searchField" #change="filter()">
</label>
<a style="float:right" href="#" data-target="#create-announcement-modal" data-toggle="modal" class="btn btn-primary">
<i class="">Add</i>
</a>
</div>
// more div or not important template code
</template>
Announcement.Vue Script Code:
<script>
import toastr from "toastr";
export default {
name: 'announcement',
data() {
return {
announcements: [],
searchField: "",
deleteCsrfToken: this.$root.csrfTokens["deleteAnnouncement"]
}
},
beforeMount: async function () {
await this.queryAnnouncements();
},
methods: {
filter: async function () {
await this.queryAnnouncements(this.searchField);
},
queryAnnouncements: async function (filter, pageSize, pageIndex, sortBy, sortType) {
var data = {
"query[general-filter]": filter,
"pagination[perpage]": !!pageSize ? pageSize : 10,
"pagination[page]": !!pageIndex ? pageIndex : 1,
"sort[field]": sortType,
"sort[sort]": !!sortBy ? sortBy : "asc"
};
let response = await axios
.get("/Announcement/QueryAnnouncements", { params: data })
this.announcements = response.data.data;
},
}
}
createAnnouncement.vue code:
<template>
<button #click="createAnnouncement()" v-bind:disabled="contentDetail === ''" class="btn btn-success">
Save</button>
//not important template codes
<template>
<script>
import toastr from "toastr";
export default {
name: 'create-announcement',
data() {
return {
contentDetail: "",
createCsrfToken: this.$root.csrfTokens["createAnnouncement"],
}
},
methods: {
createAnnouncement: async function () {
var self = this;
var data = {
content: this.contentDetail,
};
let response = await axios
.post("/Announcement/createAnnouncement",
data,
{
headers: {
RequestVerificationToken: self.createCsrfToken
}
})
if (response.status == 200) {
$("#create-announcement-modal .close").click()
$("#create-announcement-form").trigger('reset');
toastr["success"]("Kayıt başarıyla eklendi!", "Başarılı!");
self.contentDetail = "";
}
else {
toastr["warning"]("Hata", "Kayıt Eklenemedi.");
}
}
},
}
</script>
Please show with sample or arrangement, my english is not very good. Thanks.
I try to use sharepoint 2013 rest api with breezejs.
I get error
A nonnullable DataProperty cannot have a null defaultValue. Name: undefined
when breezejs validates returned metadata (_api/$metadata).
With breeze.EntityManager("/_vti_bin/listdata.svc/") all works.
What are the ways to fix it?
<script src="/_layouts/15/SharePointProject7/scripts/angular.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/q.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/datajs-1.1.1.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/breeze.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/breeze.toq.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/app.js"></script>
<div data-ng-app="app">
<div data-ng-controller="Ctrl2">
<ul>
<li data-ng-repeat="c in customers"><input type="text" data-ng-model="c.Title" /></li>
</ul>
<button type="button" data-ng-click="save()">Save</button>
</div>
</div>
var app = angular.module('app', []);
app.run(['$q', '$rootScope', function ($q, $rootScope) {
breeze.core.extendQ($rootScope, $q);
}]);
app.service('listData', function () {
breeze.config.initializeAdapterInstances({ dataService: "OData" });
var manager = new breeze.EntityManager("/_api/");
var changes;
this.getItems = function () {
var query = breeze.EntityQuery.from("Lists");
return manager.executeQuery(query).to$q();
};
this.saveItems = function () {
if (manager.hasChanges()) {
changes = manager.getChanges();
manager.saveChanges().to$q(saveSucceeded, saveFailed);
}
else {
alert("Nothing to save");
};
};
function saveSucceeded() {
alert("OK");
};
function saveFailed(error) {
alert(error.message);
};
});
app.controller('Ctrl2', function ($scope, listData) {
function initialize() {
listData.getItems().then(querySucceeded, _queryFailed);
};
function querySucceeded(data) {
$scope.customers = data.results;
}
function _queryFailed(error) {
alert(error.message);
}
$scope.save = function () {
listData.saveItems();
};
initialize();
});
It is better to hand write the entity configuration as the SP metadata can be huge and it is not a good idea to pull it down at the beginning of the app load.
First get the SharePoint breeze adapter from breeze labs.
Full details here http://www.andrewconnell.com/blog/getting-breezejs-to-work-with-the-sharepoint-2013-rest-api
That post goes step by step on getting this working.