I'm trying to access xhttp.responseText by calling it from another component but it shows undefined. Than I tried to access it from outside xtthp.onreadystatechange. Again it shows undefined. Kindly help me. I want to access it from another Component(login.component.ts).
Here's the files.
filedata.component.ts
import { Component, OnInit } from '#angular/core';
#Component(
{
selector: "file",
templateUrl: "./filedata.component.html",
})
export class FileData
{
makeRequest(): string
{
let input;
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function()
{
if (this.readyState === 4 && this.status === 200)
{
// here it's good
document.getElementById("demo").innerHTML = this.responseText;
input = this.responseText;
}
};
document.getElementById("demo").innerHTML = input; //here it's undefined
xhttp.open("GET", 'http://localhost/Angular-cli/login/employees.txt', true);
xhttp.send();
return input; //I want to call it from login.component.ts
}
}
filedata.component.html
<div>
<button type = "button" (click) = "makeRequest()">File Test</button>
<p id = "demo"></p>
</div>
login.component.ts
import { Component, OnInit } from '#angular/core';
import { AdminAccount } from '../admin/admin.component';
import { Router } from "#angular/router";
import {ReactiveFormsModule, FormsModule} from "#angular/forms";
import { FileData } from "../filedata/filedata.component";
#Component(
{
selector: "login",
templateUrl: "./login.component.html"
})
export class LoginForm
{
data = {username: "", password: ""};
input = {username: "", password: ""};
constructor(private router: Router, private filedata: FileData){}
formSubmit()
{
console.log("Input file data for login:", typeof(this.filedata.makeRequest()));
if(this.filedata.makeRequest()) //here it is undefined.
{
this.input.username = this.filedata.makeRequest().split("??")[0];
this.input.password = this.filedata.makeRequest().split("??")[1];
if(this.data.username == this.input.username && this.data.password == this.input.password)
{
this.router.navigate(["/admin"]);
}
else
console.log("Wrong User or Pass");
}
else
console.log("Undefined!");
this.data.username = "";
this.data.password = "";
}
}
I want to access responseText here by calling makeRequest. Any suggestions what's going on? What should I do to access responseText here.
this.input.username = this.filedata.makeRequest().split("??")[0];
this.input.password = this.filedata.makeRequest().split("??")[1];
if(this.data.username == this.input.username && this.data.password ==
this.input.password)
{
this.router.navigate(["/admin"]);
}
UPDATED: I think you need return async function. Try like this:
makeRequest(): any {
return new Promise(resolve => {
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET", 'http://localhost/Angular-cli/login/employees.txt', true);
xhr.onreadystatechange = () => {if(xhr.readyState === 4 && xhr.status === 200) resolve(xhr.responseText);};
xhr.send();
});
}
then use this function:
this.filedata.makeRequest().then(res => { if(res) {/* do what u want */} })
UPDATED 2: better use requests like this:
import { Http, RequestOptions, Response, Headers} from '#angular/http';
import { Observable } from "rxjs";
constructor(private http: Http){}
functionName() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ withCredentials: true, headers });
return this.http.get(`your url here`, options)
.map((res: Response) => {
return res.json();
})
.catch((error: Response) => {
return Observable.throw(`error: ${error}`);
})
}
then call function in component
Related
I have a GET request with axios and get a .png file back and want to show this inside my template. I can't use a path url, because the image is each time differently.
This is my fastapi route.
from io import BytesIO
from fastapi.responses import Response
#app.get("/image", response_class=Response)
def load_image():
...
buffer = BytesIO()
img.save(buffer, format="PNG")
return Response(content=buffer.getvalue(), media_type="image/png")
This is the vue component:
<script>
export default {
name: "Example",
data() {
return {
image: null;
};
},
methods: {
async loadImage() {
const url = "/image";
const response = await $axios.get(url, { responseType: "arraybuffer" });
if (response.status == 200) {
const base64string = btoa(String.fromCharCode(...new Uint8Array(response.data)));
console.log(base64string); // -> this is a empty string
this.image = 'data:image/png;base64,' + base64string;
}
},
mounted() {
this.loadImage();
},
};
</script>
<template>
<div>
<img :src="image" title="Image" />
</div>
</template>
You can...
get the data as a blob by passing { responseType: "blob" } to axios
convert the blob to base64 with FileReader (used blobToData function from https://stackoverflow.com/a/63372663/197546)
use the base64 data as the image src
example:
const app = Vue.createApp({
data() {
return {
imageSrc: null,
};
},
methods: {
async loadImage() {
const url =
"https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
const response = await axios.get(url, { responseType: "blob" });
if (response.status == 200) {
const base64data = await blobToData(response.data);
this.imageSrc = base64data;
}
},
},
mounted() {
this.loadImage();
},
});
app.mount("app");
function blobToData(blob) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.readAsDataURL(blob)
})
}
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios#0.27.2/dist/axios.min.js"></script>
<app>
<img :src="imageSrc" v-if="imageSrc"/>
</app>
As Chris pointed out, you can also...
get the data as an array buffer by passing { responseType: "arraybuffer" } to axios
convert array to base64 data using btoa(String.fromCharCode(...new Uint8Array(response.data)))
build the src data by adding prepending the content type to the base64 data
example:
const app = Vue.createApp({
data() {
return {
imageSrc: null,
};
},
methods: {
async loadImage() {
const url =
"https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
const response = await axios.get(url, { responseType: "arraybuffer" });
if (response.status == 200) {
const b64 = btoa(String.fromCharCode(...new Uint8Array(response.data)));
const imgData = "data:" + response.headers['content-type'] + ";base64," + b64;
this.imageSrc = imgData;
}
},
},
mounted() {
this.loadImage();
},
});
app.mount("app");
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios#0.27.2/dist/axios.min.js"></script>
<app>
<img :src="imageSrc" v-if="imageSrc"/>
</app>
I try to set up plugin ckeditor/ckeditor5-export-pdf on my Laravel App But I cant do this. I still get issues like: Uncaught TypeError: Failed to resolve module specifier "#ckeditor/ckeditor5-export-pdf/src/exportpdf". Relative references must start with either "/", "./", or "../".
I did all steps as in docs: https://ckeditor.com/docs/ckeditor5/latest/features/export-pdf.html#configuration But when I try use import ExportPdf from '#ckeditor/ckeditor5-export-pdf/src/exportpdf'; I get the error like above. Please help. Maybe some have stuck on this issue before
import ExportPdf from '#ckeditor/ckeditor5-export-pdf/src/exportpdf';
console.log(ExportPdf);
$(document).ready(function () {
/*function ExportPdf(editor) {
editor.execute('exportPdf');
}*/
function SimpleUploadAdapter(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = function(loader) {
return {
upload: function() {
return loader.file
.then(function (file) {
return new Promise(function(resolve, reject) {
// Init request
var xhr = new XMLHttpRequest();
xhr.open('POST', '/admin/instructions/ckmedia', true);
xhr.setRequestHeader('x-csrf-token', window._token);
xhr.setRequestHeader('Accept', 'application/json');
xhr.responseType = 'json';
// Init listeners
var genericErrorText = `Couldn't upload file: ${ file.name }.`;
xhr.addEventListener('error', function() { reject(genericErrorText) });
xhr.addEventListener('abort', function() { reject() });
xhr.addEventListener('load', function() {
var response = xhr.response;
if (!response || xhr.status !== 201) {
return reject(response && response.message ? `${genericErrorText}\n${xhr.status} ${response.message}` : `${genericErrorText}\n ${xhr.status} ${xhr.statusText}`);
}
$('form').append('<input type="hidden" name="ck-media[]" value="' + response.id + '">');
resolve({ default: response.url });
});
if (xhr.upload) {
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
loader.uploadTotal = e.total;
loader.uploaded = e.loaded;
}
});
}
// Send request
var data = new FormData();
data.append('upload', file);
data.append('crud_id', {{ $instruction->id ?? 0 }});
xhr.send(data);
});
})
}
};
}
}
var allEditors = document.querySelectorAll('.ckeditor');
for (var i = 0; i < allEditors.length; ++i) {
ClassicEditor.create(
allEditors[i], {
extraPlugins: [SimpleUploadAdapter, /*ExportPdf*/],
/*toolbar: [
'exportPdf', '|',
],
exportPdf: {
stylesheets: [
'./path/to/fonts.css',
'EDITOR_STYLES',
'./path/to/style.css'
],
fileName: 'my-file.pdf',
converterOptions: {
format: 'A4',
margin_top: '20mm',
margin_bottom: '20mm',
margin_right: '12mm',
margin_left: '12mm',
page_orientation: 'portrait'
}
}*/
}
);
}
});
</script>```
I solved my problem with https://ckeditor.com/ckeditor-5/online-builder/ Builded what I want and setup it on my App
I try to do a single post request to upload multiple files. Now I have a functionally method that works for multiple files. But I want a single request.
submitFile(){
this.contract_file.forEach((file) =>{
let formData = new FormData();
formData.append('file', file.file);
axios.post('contracts/uploadfile/' + this.id_contract,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
}
}
).then(function(){
//
})
.catch(function(){
//
});
})
},
public function uploadFile(Request $request, Contract $contract)
{
$filename = $request->file('file')->getClientOriginalName();
$path = $request->file('file')->store($contract->id,'uploads');
$contractFile = new ContractFile();
$contractFile->fill([
'contract_id' => $contract->id,
'name' => $filename,
'path' => $path,
])->save();
}
Update:
This is what I changed,but..
let formData = []
this.contract_file.forEach((file,index) =>{
formData[index] = new FormData();
formData[index].append('file', file.file);
})
foreach($request->file('file') as $file){
//same code but I use $fille
}
Message:
Missing boundary in multipart/form-data POST data in Unknown
Update2:
<file-upload
class="btn btn-primary"
:multiple="true"
:drop="true"
:drop-directory="true"
v-model="files"
#input-filter="inputFilter"
#input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
My answer is not properly tested since I had to adapt my code. Let me know if it doesn't work or if I'm missing something.
Basically, I built my own FormData to be more flexible and easier to reuse.
Form.vue
<template>
<div>
<input #change="upload($event)"
type="file"
name="picture"
id="new-file"
class="custom-file-input"
aria-label="picture"
multiple
>
<label class="custom-file-label" for="new-file">
<span>File...</span>
<span class="btn-primary">Browse</span>
</label>
<button #click="submit" type="button" >Submit</button>
</div>
<template>
<script>
import MyFormData from "./MyFormData";
export default {
data() {
return {
form: new MyFormData({contract_id: 5, files: []})
}
},
methods: {
upload(event) {
for (let file of event.target.files) {
try {
let reader = new FileReader();
reader.readAsDataURL(file); // Not sure if this will work in this context.
this.form.files.push(file);
} catch {}
}
},
submit(){
this.form.post('/my-url')
.catch(errors => {
throw errors;
})
.then((response) => location = response.data.redirect);
}
}
}
</script>
MyFormData.js
export default class MyFormData {
constructor(data, headers) {
// Assign the keys with the current object MyFormData so we can access directly to the data:
// (new FormData({myvalue: "hello"})).myvalue; // returns 'hello'
Object.assign(this, data);
// Preserve the originalData to know which keys we have and/or reset the form
this.originalData = JSON.parse(JSON.stringify(data));
this.form = null;
this.errors = {};
this.submitted = false;
this.headers = headers || {}
}
// https://stackoverflow.com/a/42483509/8068675
// It will build a multi-dimensional Formdata
buildFormData(data, parentKey) {
if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File) && !(data instanceof Blob)) {
Object.keys(data).forEach(key => {
this.buildFormData(data[key], parentKey ? `${parentKey}[${key}]` : key);
});
} else {
const value = data == null ? '' : data;
this.form.append(parentKey, value);
}
}
// Returns all the new / modified data from MyFormData
data() {
return Object.keys(this.originalData).reduce((data, attribute) => {
data[attribute] = this[attribute];
return data;
}, {});
}
post(endpoint) {
return this.submit(endpoint);
}
patch(endpoint) {
return this.submit(endpoint, 'patch');
}
delete(endpoint) {
return axios.delete(endpoint, {}, this.headers)
.catch(this.onFail.bind(this))
.then(this.onSuccess.bind(this));
}
submit(endpoint, requestType = 'post') {
this.form = new FormData();
this.form.append('_method', requestType);
this.buildFormData(this.data());
return axios.post(endpoint, this.form, {
headers: {
'Content-Type': `multipart/form-data; boundary=${this.form._boundary}`,
}
})
.catch(this.onFail.bind(this))
.then(this.onSuccess.bind(this));
}
onSuccess(response) {
this.submitted = true;
this.errors = {};
return response;
}
onFail(error) {
console.log(error);
this.errors = error.response.data.errors;
this.submitted = false;
throw error;
}
reset() {
Object.assign(this, this.originalData);
}
}
Edit Based on your note specifying you're using vue-upload-component
Your submit method should look like this
submitFile(){
let files = this.contract_file.map((obj) => obj.file));
let form = new MyFormData({files: files});
form.post('contracts/uploadfile/' + this.id_contract)
.then(function(){
//
})
.catch(function(){
//
});
},
In your controller
public function uploadFile(Request $request, Contract $contract) {
if($request->hasFile('files')){
$files = $request->file('files');
foreach ($files as $file) {
$filename = $file->getClientOriginalName();
$path = $file->store($contract->id,'uploads');
$contractFile = new ContractFile();
$contractFile->fill([
'contract_id' => $contract->id,
'name' => $filename,
'path' => $path,
])->save();
}
}
}
Adding the boundary to the Content-Type header fixed my problem. You can do it like below. Just change only submitFile() function.
submitFile(){
this.contract_file.forEach((file) =>{
let formData = new FormData();
formData.append('file', file.file);
axios.post('contracts/uploadfile/' + this.id_contract,
formData,
{
headers: {
'Content-Type': 'multipart/form-data;boundary=' + Math.random().toString().substr(2),
}
}
).then(function(){
//
})
.catch(function(){
//
});
})
},
I'm trying to upload Image of 4160 * 3120 size and using ng2 Image cropper to maintain the ratio of uploaded Image. but it is uploading fix sized (750* 400 ) Image every time regardless of the actual size of the cropper.
this.cropperSettings2 = new CropperSettings();
this.cropperSettings2.width = 750;
this.cropperSettings2.height = 400;
this.cropperSettings2.keepAspect = true;
this.cropperSettings2.croppedWidth = 750;
this.cropperSettings2.croppedHeight = 400;
this.cropperSettings2.canvasWidth = 427.367;
this.cropperSettings2.canvasHeight = 224;
this.cropperSettings2.minWidth = 750;
this.cropperSettings2.minHeight = 400;
this.cropperSettings2.rounded = false;
this.cropperSettings2.minWithRelativeToResolution = false;
this.cropperSettings2.cropperDrawSettings.strokeColor = 'rgba(255,255,255,1)';
this.cropperSettings2.cropperDrawSettings.strokeWidth = 2;
this.cropperSettings2.noFileInput = true;
this.data2 = {};
Is it a bug or I'm doing something wrong?
In your HTML, you have onCrop event
<div>
<img-cropper [image]="data1" [settings]="cropperSettings1" (onCrop)="cropped($event)"></img-cropper>
<span class="result" *ngIf="data1.image" >
<img [src]="data1.image" [width]="cropperSettings1.croppedWidth" [height]="cropperSettings1.croppedHeight">
</span>
<button class="btn-primary" (click)="finalCropped($event)">Upload</button>
In your .ts file define that method named cropped()
cropped(bounds: Bounds) {
this.cropperSettings1.croppedHeight = bounds.bottom - bounds.top;
this.cropperSettings1.croppedWidth = bounds.right - bounds.left;
console.log("cropped width: " + this.cropperSettings1.croppedWidth);
console.log("cropped height: " + this.cropperSettings1.croppedHeight);
}
finalCropped(){
// upload cropped image using some service.
this.uploadStr(this.data1.image);
}
uploadStr(base64Str: string) {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", (ev: ProgressEvent) => {
//You can handle progress events here if you want to track upload progress (I used an observable<number> to fire back updates to whomever called this upload)
console.log(ev);
this.percentageUpload = (ev.loaded / ev.total * 100).toFixed(1).toString() + " %"
});
xhr.upload.addEventListener('loadstart', (ev: ProgressEvent) => {
// When the request starts.
this.showProgressBar = true;
console.log(ev);
});
xhr.upload.addEventListener('load', (ev: ProgressEvent) => {
// When the request has *successfully* completed.
// Even if the server hasn't responded that it finished.
this.showProgressBar = false;
console.log(ev);
});
xhr.upload.addEventListener('loadend', (ev: ProgressEvent) => {
// When the request has completed (either in success or failure).
// Just like 'load', even if the server hasn't
// responded that it finished processing the request.
setTimeout(function () {
alert('Upload complete, please save profile settings ');
}, 1000);
this.showProgressBar = false;
// this.currentProject.Logo = ;
console.log(ev);
});
xhr.upload.addEventListener('error', (ev: ProgressEvent) => {
// When the request has failed.
this.showProgressBar = false;
console.log(ev);
});
xhr.upload.addEventListener('abort', (ev: ProgressEvent) => {
// When the request has been aborted.
// For instance, by invoking the abort() method.
this.showProgressBar = false;
console.log(ev);
});
xhr.upload.addEventListener('timeout', (ev: ProgressEvent) => {
// When the author specified timeout has passed
// before the request could complete.
this.showProgressBar = false;
console.log(ev);
});
var blobObj = this.convertToBlob(base64Str);
this.s3urlService.getPreSignedURL("project")
.subscribe(data => {
this.s3data = data;
this.setFormImageUrl(this.s3data.fileName);
console.log("Url to upload: " + this.s3data.fileUploadUrl);
xhr.open("PUT", this.s3data.fileUploadUrl, true);
xhr.send(blobObj);
})}
aws s3 service class to upload data to s3.
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { ApiService } from './api.service';
import { JwtService } from './jwt.service';
import { S3Url } from '../models';
#Injectable()
export class S3urlService {
constructor (
private apiService: ApiService,
private jwtService: JwtService
) {}
getPreSignedURL(imageType?:string): Observable<S3Url> {
let requestBody = {
Username:this.jwtService.getUsername(),
FileExt:"jpg",
ImageType: imageType
}
//console.log("in getPreSignedURL");
return this.apiService.post('/',requestBody,2)
.map(data => {
return data;
});
}}
Hope, this solves your problem.
I am using angular2 with asp.net core webapi. Using the following code to send file information. IFormFile is always null. I have used same name in post input and in api method parameter, still no luck. Please help me.
FormData
this.formData.append("file", f,f.name);
Component method
public UploadFiles() {
console.log("Form Data:" + this.formData);
let saved: boolean = false;
this.claimsService
.UploadFiles(this.formData)
.subscribe(data => {
saved = data;
}, error => {
console.log(error)
swal(error);
})
}
Service Method
UploadFiles(data: FormData): Observable<boolean> {
return this.ExecuteFilePost("Upload/Upload", data);
}
Base Service Method:
public ExecuteFilePost(action: string, data: FormData) {
let _body = data;
let headers = new Headers({ 'Content-Type': undefined});
let url = this._baseUrl + action;
let requestoptions: RequestOptions = new RequestOptions({
headers: headers,
});
console.log('req url:' + url);
return this.http.post(url,_body,requestoptions)
.share()
.map((res: Response) => {
if (res.status < 200 || res.status >= 300) {
throw new Error('This request has failed ' + res.status);
}
else {
return res.json();
}
});
}
WebApi Method
[HttpPost]
[ActionName("Upload")]
public async Task Upload(IFormFile file)
{
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
// foreach (var file in files)
// {
if (file.Length > 0)
{
using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
// }
}
Chrome request screens
enter image description here
I know the post is old, but maybe for others. You can do things like this:
HTML:
<input #fileInput type="file" multiple />
<button (click)="addFile()">Add</button>
Component:
#ViewChild("fileInput") fileInput;
addFile(): void {
let fi = this.fileInput.nativeElement;
if (fi.files) {
let fileToUpload = fi.files;
this.settingsService
.upload(fileToUpload)
.subscribe(res => {
console.log(res);
});
}
}
Service:
upload(files: any) {
let formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append("files", files[i]);
}
return this.http
.post(fileUploadApi, formData)
.map(response => this.extractData(response as Response))
.catch(error => this.httpErrorHandlerService.responseError(error));
}
Web API
[HttpPost]
[Route("Upload")]
public IActionResult UploadFiless([FromForm] IFormFileCollection files)
{
try
{
return this.Ok();
}
catch (Exception)
{
return this.BadRequest();
}
}