I'm trying to load a multipage pdf from an API into vue.js using the vue-pdf library.
I have can successfully load the pdf and render the 1st page as described in this earlier question here
I've followed the instructions given in The Documentation and tried a with / without using a loadingTask and using different options for the number of pages.
I've attached the code below. this version shows no page number and no pdf. The console has the "loadPdf called" and "success" log messages and no errors.
<template>
<div>
<h3>page count is {{ numPages }}</h3>
<pdf v-for="i in numPages" :key="i" :page="i" :src="pdfsrc"></pdf>
</div>
</template>
<script>
import pdf from "vue-pdf";
import axios from "axios";
import ActiveDirectoryService from "#/services/ActiveDirectoryService.js";
export default {
components: {
pdf
},
props: {
exportId: String
},
data() {
return {
pdfsrc: null,
numPages: null,
objectUrl: null
};
},
mounted() {
if (this.pdfsrc === null) return;
this.pdfsrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
},
methods: {
loadPdf() {
console.log("loadPdf called");
return axios
.get(`${process.env.VUE_APP_API_INTRANET}/pdf`, {
responseType: "blob"
})
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
})
.catch(console.error); //
},
clearBuffer() {
if (this.objectUrl !== null) {
URL.revokeObjectURL(this.objectUrl);
this.pdfsrc = null;
this.objectUrl = null;
}
}
},
watch: {
exportId: function(oldVal, newVal) {
console.log("id Changed api for pdf.");
this.loadPdf();
}
},
created() {
console.log("created with id ", this.exportId);
this.loadPdf();
},
beforeDestroy() {
this.clearBuffer();
}
};
</script>
<style scoped></style>
I'm testing with a small 3 page pdf at 46kb in total. The closest I have come to getting this to work is to hard code the number of pages to 3 which renders the pdf correctly. eg
<pdf v-for="i in 3" :key="i" :page="i" :src="pdfsrc"></pdf>
It works but obviously not a solution!
I've also tried other ways to load 'numPages' such as adding this to pdf
#num-pages="numPages = $event"
and using a computed that returns this.pdf.numPages
but so far all have failed.
I've also tried moving the promise to get numpages into the loadPdf method like so
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
this.pdfsrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
})
.catch(console.error); //
which also doesn't work.
Update
I've peppered the code with log statements as requested. All looks like it should be working.
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
console.log("this.pdfsrc.promise", this.pdfsrc.promise);
this.pdfsrc.promise.then(pdf => {
console.log("pdf in callback", pdf);
this.numPages = pdf.numPages;
console.log(
`this.numPages: ${this.numPages} pdf.numPages: ${pdf.numPages} `
);
console.log(this);
});
})
Update 2
I've cleaned up my example and upon removing this line
#num-pages="numPages = $event"
from the template it all started working! I've traced back through the changes in code and it seems that wrapping the object url in a loadingTask and moving the promise to the loadPdf() method is what fixed it.
Related
In a Vue.js application, I have fetched a product based on provided route id.
Product object in an array has pictures.
I want to create a copy of reactive data "mosaicImagesList" that is reflected once the product is fetched, and it should have following structure:
[
{
"url": "https://via.placeholder.com/650x250",
"isWide": true,
"isTall": false
},
{
"url": "https://via.placeholder.com/350x150",
"isWide": true,
"isTall": false
}
]
It shows an output in console.log, but it shows an empty list of array in the rendered page.
Here is my code:
export default {
name: 'products.show',
data () {
return {
slide: 0
}
},
computed: {
product () {
return this.$store.state.product
},
images () {
const items = this.product.picture || []
const result = []
items.forEach(image => {
const img = new Image()
img.addEventListener('load', function () {
const isWide = img.width > img.height
const isTall = img.height > img.width
result.push({ url: image, isWide, isTall })
})
img.src = image
})
console.log(result) // <--- THIS WORKS IN THE CONSOLE LOG DEV TOOL
return result
}
},
methods: {
async fetchProduct () {
await this.$store.dispatch('products/getById', this.$route.params.id)
}
},
async created () {
await this.fetchProduct()
}
}
Here is the HTML section:
<div class="image-mosaic">
<div
v-for="(image, index) in images"
:key="index"
:style="`background-image: url('${image.url}')`"
class="card" />
</div>
The for..in-loop iterates over the enumerable properties of an Object.
As images is an Array the solution might be to use a for..of-loop.
My async actions do not run correctly. Im new to Vue and JS and I am not sure what is happening here. I placed some confirm() dialogs within my code, to see which line passed and which not.
Within the ScanView.vue I call my addProduct action. I get the confirm dialog saying "addProduct" and dispatch calles the next callAPI action where I get the "callAPI" confirm dialog but nothing more. Seems like fetch() isnt working at all, because no any other dialog is shown. What am I doing wrong?
ScanView.vue
export default defineComponent({
name: "Home",
methods: {
scanEan() {
// QR Code Scanner Logic
this.$store.dispatch("addProduct", ean);
}
});
main.js
const store = new Vuex.Store({
state: {
products: [{
name: 'Produkt',
ean: '123',
amount: '1',
smallImageUrl: 'smImage',
mediumImageUrl: 'mdImage',
largeImageUrl: 'lgImage',
expiration: []
}]
},
mutations: {
addProduct(state, product) {
state.products.unshift(product);
}
},
actions: {
addProduct(context, ean) {
confirm("addProduct: " + ean);
context.dispatch('callAPI', ean);
},
callAPI(context, ean) {
confirm("callAPI: ");
fetch("https://world.openfoodfacts.org/api/v0/product/" + ean + ".json") //
.then(response => {
confirm("reesponse");
return response.json();
}
) //
.then(data => {
confirm("data: " + data);
context.dispatch('saveProduct', data);
});
},
saveProduct(context, data) {
confirm("saveProduct: ");
const name = data.product.product_name;
const ean = data.code;
const smImage = data.product.image_front_thumb_url;
const mdImage = data.product.image_front_small_url;
const lgImage = data.product.image_front_url;
const expiration = new Array();
const date = new Date(data.product.expiration_date);
expiration.push(date);
const product = new Product(
name,
ean,
smImage,
mdImage,
lgImage,
expiration
)
confirm("Produktdata: " + product);
context.commit('addProduct', product);
}
}
});
app.use(store);
EDIT
I build a simulate button for better testing. QR Scanning does not work in Browser.
Result It does work in Browser. But not on my emulator or android device. Seems like fetch() isnt the right way with ionic-vue. If I catch the error I got TypeError: Failed to fetch...
<template>
<button #click="simulateScan">Simulate Scan</button>
</template>
<script>
export default {
methods: {
simulateScan() {
this.$store.dispatch('addProduct', 737628064502);
}
}
};
</script>
Final Solution
fetch() does not work on android. You have to use something like cordova-http, capacitor-http, ionic-http or else. I used capacitorcommunity-http.
npm install #capacitor-community/http
npx cap sync
import { Http } from '#capacitor-community/http';
[...]
callAPI(context, ean) {
var eanurl = "https://world.openfoodfacts.org/api/v0/product/" + ean + ".json";
Http.get({ url: eanurl}) //
.then(response => {
return response.data;
}
) //
.then(data => {
console.log(data);
context.dispatch('saveProduct', data);
}).catch(error => confirm(error));
},
[...]
I'm trying to upload image using vue.js in Laravel for that i'm using this link
https://jsfiddle.net/b412ruzo/
to upload image using vue.js and when i submit the form i'm getting following image in files array
now issue is that i cannot get this file array in laravel controller
when i print
$request->file('files')
in my controller i am getting null.
and when i print $request->input('files') this is the result, an empty array
Any help regarding this issue is highly appreciated.
Code Snippet :
data() {
return {
rawData: [],
formData: new Form({
files:[],
})
..
const header = {
Authorization: "Bearer " + this.token,
};
this.formData
.post(APP_URL + `/api/post`, { headers: header })
.then((response) => {
}
not sure you can send ajax request via this.formData.post
try this
new Vue({
el: "#app",
data() {
return {
option: {
maxFileCount: 3
},
files:[],
rawData: [],
}
},
methods: {
loaddropfile: function(e) {
e.preventDefault()
e.stopPropagation()
alert('ok')
console.log(e)
},
openinput: function() {
document.getElementById("vue-file-upload-input").click();
},
addImage: function(e) {
const tmpFiles = e.target.files
if (tmpFiles.length === 0) {
return false;
}
const file = tmpFiles[0]
this.files.push(file)
const self = this
const reader = new FileReader()
reader.onload = function(e) {
self.rawData.push(e.target.result)
}
reader.readAsDataURL(file)
},
removeFile: function(index) {
this.files.splice(index, 1)
this.rawData.splice(index, 1)
document.getElementById("vue-file-upload-input").value = null
},
upload: function() {
alert('Check console to see uploads')
console.log(this.files)
axios.post(`${APP_URL}/api/post`,{files:this.files},{ headers: header })
.then((response) => {});
}
},
mounted(){
}
})
it will send your form data to files key so you can get all the files via $request->file('files')
I have this _id.vue page on my Nuxt.js project:
<template>
<div class="wrapper">
<HeaderApp>
<DivHeaderMenu>
</DivHeaderMenu>
</HeaderApp>
<CenterContentDinamicFirmenistorieApp>
</CenterContentDinamicFirmenistorieApp>
<FooterApp>
</FooterApp>
</div>
</template>
<script>
//company_history
import axios from 'axios';
import HeaderApp from '~/components/HeaderApp';
import FooterApp from '~/components/FooterApp';
import CenterContentDinamicFirmenistorieApp from '~/components/CenterContentDinamicFirmenistorieApp'
import DivHeaderMenu from '~/components/DivHeaderMenu';
import Pixelperfect from '~/components/Pixelperfect';
export default{
async fetch ({ store, params, redirect, app}) {
return axios.get('http://seo-gmbh.eu/json/api_sunds.php?action=get_pages&url=company_history')
.then((res) => {
store.commit('company_history/init_data_for_firmenistorie', res.data);
})
},
async validate({store, params, redirect}) {
const urlData = store.state.company_history.dbFirmenstorie.dbFirmenistorieSortArrayData;
let resultArray = false;
for (let i = 0; i < urlData.length; i++) {
if(params.id === urlData[i].toString()){
return resultArray = urlData[i];
}
}
if(resultArray == false){
return redirect('/Firmenistorie');
}
},
head () {
return {
title: this.$store.state.company_history.dbFirmenstorie.dbFirmenistorieData.data.meta.title,
meta: [
{description: this.$store.state.company_history.dbFirmenstorie.dbFirmenistorieData.data.meta.description}
]
}
},
components:{
HeaderApp,
FooterApp,
CenterContentDinamicFirmenistorieApp,
DivHeaderMenu,
Pixelperfect
},
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
My task is to get a redirect when I get the 404th error on the dynamic page (_id). The whole implementation works fine if I go through nuxt-link (s) to similar pages - the 404th error works fine if I enter an incorrect URL in the address bar. But the problem appears if I'm already on a working page - I reload it. Instead of loading the same page again, I get the 404th error and redirect as a result. This happens because in this particular case I do not receive data from the store
My question is: How can I solve this (asynchronous, as I understand it) problem? (I tried everything that is possible - nothing helps).
My Vuex repository looks rather piled up - but just in case, I'll throw its code for a better understanding of the problem:
export const state = () => ({
dbFirmenstorie: {
dbFirmenistorieData: null,
dbFirmenistorieMaxYearData: null,
dbFirmenistorieMaxDetailsData: null,
dbFirmenistorieSortArrayData: [],
},
});
export const mutations = {
init_data_for_firmenistorie (state, uploadDbFirmenistorieData) {
state.dbFirmenstorie.dbFirmenistorieData = uploadDbFirmenistorieData;
state.dbFirmenstorie.dbFirmenistorieData.data.content_json = JSON.parse(state.dbFirmenstorie.dbFirmenistorieData.data.content_json);
state.dbFirmenstorie.dbFirmenistorieData.data.meta = JSON.parse(state.dbFirmenstorie.dbFirmenistorieData.data.meta);
for (let i = 0; i < state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data.length; i++) {
if(state.dbFirmenstorie.dbFirmenistorieSortArrayData.indexOf( Number( state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i].company_history_from_year )) == -1 ){
state.dbFirmenstorie.dbFirmenistorieSortArrayData.push(Number(state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i].company_history_from_year));
}
if(state.dbFirmenstorie.dbFirmenistorieMaxYearData < Number(state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i].company_history_from_year)){
state.dbFirmenstorie.dbFirmenistorieMaxYearData = Number(state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i].company_history_from_year);
state.dbFirmenstorie.dbFirmenistorieMaxYearData = Number(state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i].company_history_from_year);
state.dbFirmenstorie.dbFirmenistorieMaxDetailsData = state.dbFirmenstorie.dbFirmenistorieData.data.company_history_data[i];
}
}
function sortNumber(a, b) {
return b - a;
}
state.dbFirmenstorie.dbFirmenistorieSortArrayData.sort(sortNumber);
}
};
I am pretty sure that if you start using catch() with axios as everyone should always do, you will be able to handle all non 200 responses just fine. Which means 404, 40x, 50x, etc...
axios
.get("https://example.com")
.then(res => console.log(res))
.catch(e => console.log(e))
I am developing hybrid mobile application.
In one of the scenario we need to fetch mimeType from a file when we select or upload a file.
I am using apache FileTransfer.
window.resolveLocalFileSystemURL(fileURI , resolveOnSuccess, resolveOnFail)
you can get it from cordova File plugin.
$cordovaFile.checkFile(uri, '')
.then(function(entry) {
// success
var name = entry.name;
entry.file(function(data) {
// get mime type
var mime = data.type;
alert(mime);
})
}, function(error) {
// error
// show toast
});
I got it working like this in TypeScript and Angular 2:
this._File.resolveLocalFilesystemUrl(somefileUri).then((entry: Entry) => {
if (entry) {
var fileEntry = entry as FileEntry;
fileEntry.file(success => {
var mimeType = success.type;
}, error => {
// no mime type found;
});
}
});
file-transfer does not expose mimeType and other FileUploadOptions params.
Mimetype autodetection is only supported for uploads in Windows plugin code.
And here is a Jira ticket for this feature CB-5946 - it also has some suggestions on Android implementation.
In Angular 2 I use this:
export class Plugins {
albums = {
open () : Promise<any> {
return ImagePicker.getPictures({
quality: 100,
maximumImagesCount: 1,
}).then((imgUrls) => {
return imgUrls;
}, (err) => {
if(err.error == "cordova_not_available") {
alert("Cordova is not available, please make sure you have your app deployed on a simulator or device");
} else {
console.log("Failed to open albums: " + err.error);
}
});
},
}
...
#Component({
templateUrl: 'build/pages/home/home.html',
directives: [UploadButton]
})
export class HomePage implements OnInit {
openAlbums = (): void => {
var $self = this;
this._plugins.albums.open().then((imgUrls) => {
imgUrls.forEach((imageUrl: string): void => {
if (imageUrl) {
window.resolveLocalFileSystemURL(imageUrl, function (entry: FileEntry) {
entry.file(file=> {
console.log('mimeType', file.type);
}, ((error:FileError) => console.log(error)));
});
}
});
}); };
resolveLocalFileSystemURL gives back through the success callback an Entry which I had to cast to FileEntry to get access to the file method which gives back a File which extends Blob that has the mime type property.