Reference https://bootstrap-vue.org/docs/components/pagination
data() {
return {
currentPage: 1,
}
computed: {
rows() {
return this.productsList.length;
},
methods() {
layoutchange() {
this.layout = !this.layout;
if (this.layout === true) {
this.listProducts();
} else {
this.gridProducts();
}
},
gridProducts() {
let project_key = 'https://.....url'
const categoryUrl = project_key + '/................offset=0&limit=12';
const sendGetRequest = async() => {
let access_token = await axios.request({
method: "get",
baseURL: categoryUrl,
headers: {
'Authorization': "Bearer " + accToken,
'Content-Type': 'application/json',
},
data: {}
}).then((response) => {
if (response) {
// this.productsList = response.body;
this.productsList = response.data.results;
// eslint-disable-next-line
console.log(this.productsList);
}
});
};
sendGetRequest();
}
listProducts() {
let project_key = 'https://.....url'
const categoryUrl = project_key + '/................offset=0&limit=8';
const sendGetRequest = async() => {
let access_token = await axios.request({
method: "get",
baseURL: categoryUrl,
headers: {
'Authorization': "Bearer " + accToken,
'Content-Type': 'application/json',
},
data: {}
}).then((response) => {
if (response) {
// this.productsList = response.body;
this.productsList = response.data.results;
// eslint-disable-next-line
console.log(this.productsList);
}
});
};
sendGetRequest();
}
<ul v-if="layout == true">
<div class="listview-plp" v-for="product in productsList" :key="product.key" id="product" :items="productsList" :current-page="currentPage">
<div class="da">sdds</div>
<b-pagination v-model="currentPage" :total-rows="rows" #change="handlePageChange"></b-pagination>
</ul>
<ul v-if="layout == false">
<div class="listview-plp" v-for="product in productsList" :key="product.key" id="product" :items="productsList" :current-page="currentPage">
<div class="da">sdds</div>
<b-pagination v-model="currentPage" :total-rows="rows" #change="handlePageChange"></b-pagination>
</ul>
I am working on pagination for grid and list view in vuejs, the problem here is i want to display pagination for both like if it is grid view-(12 items per page) and if it is list view(8 items per page), All this i am getting from an api with offset and limit values, So i need to call an API for each page. to get it work. So i am not sure how to call api for each page using and to handle in for loop so (in my case layoutchange() method)
in grid view page1 (offset0, limit 12) page2-(offset12, limit 24) page3 (offset24 limit 36)....
in list view page1 (offset0, limit 8) page2-(offset8, limit 16) page3 (offset16 limit 24)....
Below i have taken two methods and called an api inside it like gridProducts and listProducts.(where the api is working fine with 12 and 8 items) initially.
So please help me with, Calling an api per each page using b-pagination, simultaneously using for loop for grid and list view
I have a react page that looks like this:
and right now when creating a new category the post request goes through to the database but the categories is not rendered again to display the new category unless you refresh the page (GET request for all categories on page start up).
SideBar.js
createNewCategory = async (input) => {
console.log("CREATING NEW: ", input);
var response = await fetch("http://localhost:8081/api/categories", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Token": 1234,
Accept: "application/json"
},
body: JSON.stringify({
title: input
})
})
let resp = await response.json();
this.setState({
categories: [...this.state.categories, resp]
})
}
CreateCategory.js
handleNewCategory = (event) => {
event.preventDefault()
this.props.createNewCategory(this.state.input)
this.setState({
input: ''
})
}
render(){
return (
<form onSubmit={this.handleNewCategory} className="new-category-form">
<h4>Create Category</h4>
<input onChange={this.handleInput} className="new-category-input" type="text" value={this.state.input} />
<input className="new-category-input" type="submit" value="Create" />
</form>
)
}
CategoriesContainer.js
function CategoriesContainer(props) {
function renderCategories(){
console.log("PROPS: ", props)
return props.categories.map(category => {
console.log("CATEACH: ", category)
return <Category key={category.id} category={category} />
})
}
return(
<div>
{renderCategories()}
</div>
)
}
At the moment if I create a new category with a name of letters I get the err
Uncaught (in promise) SyntaxError: Unexpected token a in JSON at position 0 sidebar.js:46
and if I create it with numbers I get
Warning: Each child in a list should have a unique "key" prop.
Im still new to react so hopefully Im not completely off the mark here, any ideas?
Fixed it. First off I was using response instead of resp to update the state and I was returning just the name rather than the whole object to the POST request.
I am trying to create an avatar editor following the Build a Forum video series.
I am on Laravel 5.8.34.
The console.log in the method #handleFileUpload(e)# shows the file uploaded.
The uploaded image appears on the page.
The console.log in the method #persist(file)# shows an empty object.
DATA FormData {}
The upload does not persist.
My Controller Method:
public function avatar_upload($id)
{
$validate = request()->validate([
'avatar' => ['required', 'image']
]);
$emp = Employee::with('user')->where('user_id', $id)->first();
$avatar = $emp->user->firstName . $emp->user->lastName . '.png';
Storage::disk('spaces')
->putFileAs('avatars', request()->file('avatar'), $avatar, 'public');
$emp->avatar = $avatar;
$emp->save();
return response([], 204);
} // end function
My Component:
<template>
<div>
<div class="text-center mb-4">
<div class="flex justify-center font-thin text-grey-dark text-2xl">
{{user.office}}
</div>
<div class="text-center">
<img class="relative rounded-lg"
:src="avatar">
</div>
<form #submit.prevent="handleFileUpload"
enctype="multipart/form-data"
v-if="canEdit">
<input
type="file"
name="avatar"
ref="file"
accept="image/png"
class="tw-input"
#change="handleFileUpload">
</form>
</div>
</div>
</template>
<script type="text/babel">
export default {
name: 'AvatarReplace',
data() {
return {
canEdit: true,
avatar: this.user.avatar
};
},
props: ['user'],
methods: {
handleFileUpload(e) {
if(! e.target.files.length) { return; } // end if
let file = e.target.files[0];
console.log('FILE', file);
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = e => {
this.avatar = e.target.result;
};
this.persist(file);
},
persist(file) {
let data = new FormData();
data.append('avatar', file);
console.log('DATA', data);
let path = `/api/staff/avatar_upload/${this.user.id}`;
axios.post(path, data)
.then((rsp) => {
//console.log(rsp);
//this.$toastr.s('File Uploaded');
});
}
}
};
</script>
This is not a normal form, Make axios knows that content-type is multipart/form-data
axios.post(path, data, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
//
});
My vue component like this :
<template>
<section>
...
<img class="media-object" :src="baseUrl+'/storage/banner/thumb/'+photo" alt="" width="64" height="64">
...
</section>
</template>
<script>
export default {
props: ['banners'],
data() {
return {
baseUrl: App.baseUrl,
bannerId: this.banners.id,
photo: this.banners.photo // result : chelsea.png
}
},
methods: {
onFileChange(e) {
let files = e.target.files,
reader = new FileReader(),
formData = new FormData(),
self = this
formData.append('file', files[0])
formData.append('banner_id', this.bannerId)
axios.post(window.App.baseUrl+'/admin/banner/upload-image',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function(response) {
if(response.data.status == 'success') {
self.photo = response.data.fileName // result : chelsea.png
}
})
.catch(function(error) {
console.log('FAILURE!!')
})
},
...
}
}
</script>
The result of :src : \my-app\storage\app\public\banner\thumb\chelsea.png
When I upload image, it will call onFileChange method. And the process upload will continue in the backend. It success upload in the folder. And the response will return same filename. So the result of response.data.fileName is chelsea.png
My problem here is : it's not update the image automatic when I upload it. When I refresh the page, the image updated
Why the image is not automatic update/changed when I upload the image?
I fixed it by doing the following, notice I added a variable named rand at the end of the photo url for cache busting. When you get a correct response from your server, simply change that variable to something unique (in this case a timestamp) and voila! your image will refresh.
<template>
<img class="media-object" :src="baseUrl+'/storage/banner/thumb/'+photo + '?rand=' + rand" alt="" width="64" height="64">
</template>
<script>
export default {
data() {
return {
rand: 1
}
},
methods: {
onFileChange(e) {
...
axios.post(url,formData).then(function(response) {
if(response.data.status == 'success') {
self.rand = Date.now()
}
})
},
...
}
}
Your images are cached by the browser.
Try to add any tag to the image like:
chelsea.png?t=<random>
The answer, as provided above, are computed properties as these designed to be reactive, but when it comes to async it best to use promises / observables. However, if you decide not use and are still experiencing problems, then you can use a loading property, like the loading property in the example below to manipulate the DOM i.e. remove the DOM with v-if when you initiate async (axios). Get and set the the image and then restore the DOM element with this.loading = true;. This forces a render of the DOM, which forces a computed property.
<template>
<section>
<div v-if="!loading">
<img class="media-object" :src="getPhoto" :alt="getAlt" width="64" height="64">
</div>
<div v-if="loading">
<!-- OR some spinner-->
<div>Loading image</div>
</div>
</section>
</template>
<script>
export default {
props: ['banners'],
data() {
return {
loading: false,
baseUrl: App.baseUrl,
bannerId: this.banners.id,
photo: {} // result : chelsea.png
}
},
computed: {
getPhoto() {
return App.baseUrl + '/storage/banner/thumb/' + this.photo.fileName;
},
getAlt() {
return photo.alt;
},
},
methods: {
onFileChange(e) {
let files = e.target.files,
reader = new FileReader(),
formData = new FormData(),
self = this
// Set loading to true
this.loading = true;
formData.append('file', files[0])
formData.append('banner_id', this.bannerId)
axios.post(window.App.baseUrl+'/admin/banner/upload-image',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function(response) {
if(response.data.status == 'success') {
self.photo = response.data.fileName // result : chelsea.png
this.loading = false;
}
})
.catch(function(error) {
console.log('FAILURE!!')
})
},
...
}
}
</script>
Just use computed property, snippet below used getImageUrl to get the updated path. I added button to trigger the mimic change on the data provided.
new Vue({
el: "#app",
data: {
baseUrl: 'baseURl', //dummy
bannerId: '', //dummy
photo: 'initPhoto.png' // dummy
},
computed: {
getImageUrl: function() {
return this.baseUrl + '/storage/banner/thumb/' + this.photo;
}
},
methods: {
mimicOnChange: function() {
this.photo = "chelsea.png"
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<span>{{ getImageUrl }}</span>
<br/>
<button #click="mimicOnChange">
On change trigger
</button>
</div>
On you above code, just use the computed directly to your src attribute:
<img class="media-object" :src="getImageUrl" alt="" width="64" height="64">
Try binding full photo's path:
<template>
<section>
...
<img v-if="photoLink" class="media-object" :src="photoLink" alt="" width="64" height="64">
<p v-text="photoLink"></p>
...
</section>
</template>
<script>
export default {
props: ['banners'],
data() {
return {
baseUrl: App.baseUrl,
bannerId: this.banners.id,
photo: this.banners.photo, // result : chelsea.png
photoLink: App.baseUrl + '/storage/banner/thumb/' + this.banners.photo
}
},
methods: {
onFileChange(e) {
let files = e.target.files,
reader = new FileReader(),
formData = new FormData(),
self = this
formData.append('file', files[0])
formData.append('banner_id', this.bannerId)
axios.post(window.App.baseUrl+'/admin/banner/upload-image',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function(response) {
if(response.data.status == 'success') {
// self.photo = response.data.fileName // result : chelsea.png
console.log('>>>INSIDE SUCCESS');
self.photoLink = self.baseUrl + '/storage/banner/thumb/' + response.data.fileName;
}
})
.catch(function(error) {
console.log('FAILURE!!')
})
},
...
}
}
I've had the same problem where inserting the same image won't trigger any action after the input. I fixed it by clearing the input.
clearInput(e) {
e.target.value = '';
},
I had some weird behaviour with vue, where after upload the img, the base64 data, was on the src img, but the browser somehow did not render it correctly, only doing any action in the form like clicking any button etc.. would magically appear.
So that was solved using a setTimeout.
uploadNewImg () {
let self = this
// Get the selected file
var file = this.$refs.profileImg.files[0]
// Create a new FileReader object
var reader = new FileReader()
reader.onload = function (e) {
// Create a new FormData object
var formData = new FormData()
formData.append('file', file)
setTimeout(function () {
self.profilePic = e.target.result// this is used as src of img tag
}, 1)
}
Looking at your question. I could not see whether you would like the process to be sync or async so I add my solution. I prefer to use Async/await in such cases and This should fix the problem of image render:
async onFileChange(e) {
let formData = new FormData();
formData.append('file', files[0]);
formData.append('banner_id', this.bannerId);
//.... Add headers and payload for post request
const {data} = await axios.post(window.App.baseUrl+'/admin/banner/upload-image', payload);
if(data.status === 'success') {
self.photo = data.fileName // result : chelsea.png
}
}
Im using https://github.com/danialfarid/ng-file-upload plugin to manage my file upload, following is my code.
HTML
<form name="form">
Single Image with validations
<div class="button" ngf-select ng-model="file" name="file" ngf-pattern="'image/*'"
ngf-accept="'image/*'" ngf-max-size="20MB" ngf-min-height="100"
ngf-resize="{width: 100, height: 100}">Select</div>
Multiple files
<div class="button" ngf-select ng-model="files" ngf-multiple="true">Select</div>
Drop files: <div ngf-drop ng-model="files" class="drop-box">Drop</div>
<button type="submit" ng-click="upload()">submit</button>
</form>
Controller
// upload on file select or drop
$scope.upload = function (file) {
file = new FormData();
file = {'file': file};
imageFind.search(file, $scope.documentsOffsetStart, $scope.titleSorting)
.then(
function (response) {
console.log('Success ' + response.config.data.file.name + 'uploaded. Response: ' + response.data);
},
function (response) {
console.log('Error status: ' + response.status);
}, function (evt) {
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
});
};
Image find service
]).factory('imageFind', [
'imageService', 'ApiService',
function (imageService, ApiService) {
return {
search: function (file, start, sort) {
var formData, params={};
if (start == null) {
start = 0;
}
if (sort == null) {
sort = "";
}
var data = {
start: start,
sort: sort
};
data = $.param(data);
var config = {'Content-Type': undefined};
return ApiService.post(imageFindPint, data, config);
}
};
}
]);
Im getting following error when image upload:
Error: 'append' called on an object that does not implement interface FormData.
jQuery.param/add
do you see I'm doing anything wrong?
Pass <button type="submit" ng-click="upload('file')">submit</button>
This should solve the issue.
In the controller
// file = new FormData();
// file = {'file': file};
can be removed but need to add FormData() to services.