I'm trying to render dinamically and image linked to an Ad in angular, the problem is that even if in tag the 'src' parameter seems to be right it can't show the image.
This is my code in Angular
ngOnChanges(changes: SimpleChanges): void {
console.log("ngOnChanges: " + changes);
console.log(this.ads.length);
for(let i = 0; i< this.ads.length; i++){
this.service.getImage(this.ads[i]).subscribe(blobList => this.ads[i].images = blobList).add( () => {
console.log("Byte: " + this.ads[i].images.at(0));
let div = document.getElementById("ad" + this.ads[i].id) as HTMLDivElement;
console.log(div);
let bytea: ArrayBuffer = this.ads[i].images.at(0) as ArrayBuffer;
const buffer = Buffer.from(bytea);
const blob = new Blob([buffer], {type: 'image/png'});
const image = this.createImageFromBlob(blob);
div.appendChild(image);
});
}
}
This is the function I use to create an HTMLImageElement from a BLOB
public createImageFromBlob(blob: Blob): HTMLImageElement {
const image = new Image();
const url = URL.createObjectURL(blob);
image.src = url;
return image;
}
This is the Ad interface
export interface Ad{
id: number;
title: string;
description: string;
user: User;
property: Property;
price: number;
mq: number;
status: string;
city: string;
images: ArrayBuffer[];
}
This is my code in HTML
<span *ngIf="!isEmpty()">
<span *ngFor="let ad of ads">
<span *ngIf="canShow(ad.status)">
<div class="container justify-content-center">
<div class="card jumbotron">
<div class="text-center badge-info badge" style="margin-top: -2%;" id="statusTitle">
<h2>{{ad.status.toUpperCase()}}</h2>
</div>
<div class="row no-gutters m-3">
<div class="col-lg-5 col-md-7 col-md-12" id="ad{{ad.id}}">
</div>
<div class="col">
<div class="card-block px-2">
<h3 class="card-title">{{ad.title}}</h3>
<p class="card-text">{{ad.description}}</p>
{{ad.price}}
<hr>
{{ad.user.nickname}}
</div>
</div>
</div>
<div class="card-footer w-100 text-muted">
Leggi annuncio
</div>
</div>
</div>
<hr>
</span>
</span>
</span>
I'm adding the java springboot controller that I use to take data from the database. This method returns a List of Byte[] 'cause I have bytea on postgres database.
#GetMapping("/getImage")
public List<byte[]> getImage(HttpServletRequest request, #RequestParam String adId){
List<Image> imageList = DBManager.getInstance().getImageDao().findByAdId(Integer.parseInt(adId));
List<byte[]> imgList = new ArrayList<>();
for (Image image : imageList) {
imgList.add(image.getData());
}
return imgList;
}
And this is the method I use in Angular to call the java server.
getImage(adId: number): Observable<Blob[]> {
return this.http.get<Blob[]>('http://localhost:8080/image', {params: {adId: adId}, responseType: 'json'});
}
As you can see i'm trying to inject the HTMLImageElement in a div linked through an ID.
I tried some other methods but this one is the only that gave me a correct Blob, but on website it only shows the "Image not Found" icon.
image
I can't figure out what I'm doing wrong, any help will be very appreciated, thanks.
I resolved ot converting the byte[] to blob but only adding the base64 to image/jpeg encoding string, thank you all, hope this will help someone.
for(let i = 0; i< this.ads.length; i++){
this.service.getImage(this.ads[i].id).subscribe(blobList => this.ads[i].images = blobList).add( () => {
let image = document.getElementById("img" + this.ads[i].id) as HTMLImageElement;
if(this.ads[i].images.at(0) == null){
image.src = "https://fakeimg.pl/400x250/?text=No%20image"
}else{
image.src = 'data:image/jpeg;base64,' + this.ads[i].images.at(0);
}
});
}
Related
I'm having this sanitize URL error in Angular, I've seen many solutions, but I've not been able to use them in my scenario, that's why I'm asking the question.
Here is my function:
#Input()
maxFileSize: number = 1000000;
public fileChangeEvent(fileInput: any) {
if (fileInput.target.files && fileInput.target.files[0]) {
const reader = new FileReader();
reader.onload = (e: any) => {
if (e.target.result) {
if (e.target.result.length < this.maxFileSize) {
this.value = e.target.result;
} else {
alert(`Logo size ${e.target.result.length} cannot exceed ${this.maxFileSize} bytes.`);
}
}
};
reader.readAsDataURL(fileInput.target.files[0]);
}
}
I've tried this.sanitizer.bypassSecurityTrustUrl(url), but in my case, fileInput.target.files[0] is a blob, so I always get an error when wrapping it worth the sanitizer function.
I'm using it in the view like this:
<ng-container *ngIf="value">
<div class="input-group-append">
<div class="img-thumbnail">
<img src="{{value}}" alt="Preview" />
</div>
<button class="btn btn-danger btn-lg" type="button" (click)="clearLogo()">
<i class="fa fa-trash" aria-hidden="true"></i>
Delete
</button>
</div>
</ng-container>
enter code here
I've also tried [src]="{{value}}", but that did not work as well.
I'm getting this error:
WARNING: sanitizing unsafe URL value
Please where am I getting it wrong?
I hope this fixes your issue.
constructor(private sanitizer:DomSanitizer){...}
....
let file = event.target.files[0];
let blob = new Blob([file], { type: file.type });
let url = window.URL.createObjectURL(blob);
this.value = this.sanitizer.bypassSecurityTrustUrl(url);
Video
I made a short 15 second Youtube video demonstrating the bug since its rather hard to explain here is video: https://www.youtube.com/watch?v=YXykw2oSh8E --- please ignore my terrible editing skills
Short Summary
I will summarize the error here: If I have two image tags and I am trying to change the image of the lower one that is located lower in the html file, the image on the higher most img tag will be changed as opposed to the one I selected even though the logic is in a seperate component instance
Code layout
In my code I have two <app-image-display> components stacked on top of one another. When I try to update the image of the one with the name of coverPhoto it will update the image in the one named profile. If I move the coverPhoto named instance higher in the html file than profile named instance and try to change profile it will update coverPhoto.
Parent
I have a parent component that looks like this:
....
<div *ngIf="company">
<app-image-display name="profile" id="profile" #profile [image]="company.profileUrl" [uploadPath]='companyProfilePath' (imageUpdated)="updateImage($event, CompanyImage.PROFILE)"></app-image-display>
<app-image-display name="coverPhoto" id="coverPhoto" #coverPhoto [image]="company.coverPhotoUrl" [uploadPath]='companyCoverPath' (imageUpdated)="updateImage($event, CompanyImage.COVER_PHOTO_URL)"></app-image-display>
</div>
.....
You can see it contains two separate instances of the same component called ImageDisplay. One of the instances is designed to handle a profile image and the other a cover photo. I put in temporary logic to show me which on is the cover photo just to be explicit as seen in the video. This component is designed to show the use the previously saved image then allow them to view a new image (tempImage) before saving it to the db. The ImageDisplayComponent looks like the following:
Image Display Html
<img [src]="image" width="200" *ngIf="!tempImage" height="200" class=" img-thumbnail" alt="Responsive
image" onerror="this.src = 'TEMP_IMG_URL_GOES_HERE'">
<img [src]="tempImage" *ngIf="tempImage" width="200" height="200" class=" img-thumbnail"
alt="Responsive image">
<span *ngIf="uploadPath.includes('cover')">Cover Image</span>
<app-image-picker *ngIf="!percentage" (imageSelected)="imageSelected($event) (tempImageSelected)="tempSelected($event)"></app-image-picker>
Image Display TS
And the .ts looks like this:
...
export class ImageDisplayComponent implements OnInit {
#Output() imageUpdated: EventEmitter<string> = new EventEmitter();
#Input() image: string = '';
#Input() uploadPath: string
tempImage: string;
percentage: Observable<number>;
selectedPictureFile: File;
task: AngularFireUploadTask;
constructor(
private dbs: AngularFireStorage,
private toastService: ToastService
) { }
ngOnInit(): void {
console.log(this.uploadPath)
}
imageSelected(image: File) {
this.selectedPictureFile = image;
this.fileUpload(this.uploadPath, this.selectedPictureFile);
}
async fileUpload(path: string, file: File): Promise<void> {
const ref = this.dbs.ref(path);
this.task = this.dbs.upload(path, file);
this.percentage = this.task.percentageChanges();
let imageUrl = '';
from(this.task).pipe(
switchMap(() => ref.getDownloadURL()),
map((img) => imageUrl = img),
finalize(() => delete this.percentage)
).subscribe(() => {
this.imageUpdated.emit(imageUrl)
},(error) => {
this.toastService.show(`${error.message}`, {
delay: 3000,
autohide: true
});
});
}
tempSelected(imageUrl: string) {
this.tempImage = imageUrl;
}
}
...
ImagePickerComponent Html
app-image-picker looks like the following:
<div style="margin: 5px">
<label for="file-upload" class="custom-file-upload">
{{labelText}}
</label>
<input type="file" id="file-upload" (change)="onPictureSelected($event)" accept=".png, .jpg">
<div class="row">
<button *ngIf="pictureSelected" class="btn btn-info m-1" [disabled]="!pictureSelected" (click)="uploadPicture()">
Save
</button>
<button *ngIf="pictureSelected" class="btn btn-warn m-1" [disabled]="!pictureSelected" (click)="cancel()">
Cancel
</button>
</div>
ImagePickerComponent TS
This is what the onPictureSelected function looks like:
// image selection and verification.
onPictureSelected(event) {
this.selectedPictureFile = event.target.files[0] as File;
const reader = new FileReader();
reader.readAsDataURL(this.selectedPictureFile);
if (!this.imageService.isImage(this.selectedPictureFile.name)) {
this.pictureSelected = false;
} else if (!this.imageService.isAllowedSize(this.selectedPictureFile.size)) {
this.pictureSelected = false;
} else {
// tslint:disable-next-line:no-shadowed-variable
reader.onloadend = (event: any) => {
if (event.target) {
this.selectedPictureURL = event.target.result;
this.tempImageSelected.emit(this.selectedPictureURL);
}
};
this.pictureSelected = true;
}
}
I am on Angular version 9.0.3
I have a simple site that is getting a list of books from the Google Books API.
I have a separate file called scripts.js that is getting all the book information (title, author, ISBN, link to the image).
I want to create a div for each book in a gallery style page, where there is a picture of the book and on top of the book is the Title, Author, and ISBN.
I've tried creating the DIV's in Javascript but I want there to be an h3, p, and img inside of each DIV and I can't seem to wrap my head around how I could do that in Javascript.
My HTML code for the gallery:
<div id="content">
<h2>My Bookshelf</h2>
<div class="book">
<!-- The book image is the background of the div -->
<h3 class="book-title">Title</h3>
<p class="book-isbn">ISBN: 000000</p>
<p class="book-author">Authors: ABC</p>
</div>
</div>
My Javascript code that cycles through the JSON file and returns the needed information.
// Returns an array with the book title, ISBN, author, bookmark icon, description, image
apiRequest.onreadystatechange = () => {
if (apiRequest.readyState === 4) {
const response = JSON.parse(apiRequest.response);
var bookList = response.items;
// Removes old search results before display new ones
bookSection.innerHTML = "";
for (let i = 0; i < bookList.length; i++) {
console.log(i);
var title = (bookList[i]["volumeInfo"]["title"]);
try {
var isbn = (bookList[i]["volumeInfo"]["industryIdentifiers"][0]["identifier"]);
} catch (TypeError) {
var isbn = "ISBN Not Available";
}
var author = (bookList[i]["volumeInfo"]["authors"]);
var description = (bookList[i]["description"]);
try {
var image = (bookList[i]["volumeInfo"]["imageLinks"]["thumbnail"]);
} catch (TypeError) {
var image = "img/unavailable.png";
}
}
}
}
You can use template literals to make your job easier.
You can do it like this:
var bookSection = `<div id="content">
<h2>My Bookshelf</h2>
<div class="book">
<!-- The book image is the background of the div -->
<h3 class="book-title">${titleVar}</h3>
<p class="book-isbn">ISBN: ${ISBNVar}</p>
<p class="book-author">Authors: ${AuthorsVar}</p>
</div>
</div>`;
Learn more about template literals from here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Your code should look something like this
apiRequest.onreadystatechange = () => {
if (apiRequest.readyState === 4) {
const response = JSON.parse(apiRequest.response);
var bookList = response.items;
// Removes old search results before display new ones
bookSection.innerHTML = "";
let bookListHtmlMarkup = '';
for (let i = 0; i < bookList.length; i++) {
console.log(i);
// Declaring book object
const book = {};
const bookListHtmlMarkup = '';
book['title'] = (bookList[i]["volumeInfo"]["title"]);
try {
book['isbn'] = (bookList[i]["volumeInfo"]["industryIdentifiers"][0]["identifier"]);
} catch (TypeError) {
book['isbn'] = "ISBN Not Available";
}
book['author'] = (bookList[i]["volumeInfo"]["authors"]);
book['description'] = (bookList[i]["description"]);
try {
book['image'] = (bookList[i]["volumeInfo"]["imageLinks"]["thumbnail"]);
} catch (TypeError) {
book['image'] = "img/unavailable.png";
}
bookListHtmlMarkup += `
<div class="book">
<div class="book-image">
<img src="${book.image}" alt="Image unavailable" />
</div>
<div class="book-info">
<h3 class="book-title">${book.title}</h3>
<p class="book-isbn">ISBN: ${book.isbn}</p>
<p class="book-author">Author: ${book.author}</p>
<p class="book-description">Author: ${book.description}</p>
</div>
</div>
`;
}
// Assigning generated markup to innerHTML of bookSection
bookSection.innerHTML = bookListHtmlMarkup;
}
}
Im a newbie to angular, currently i have created a datatable using html and angular, can you guys help me on how to bind a JSON data into this table... my scripts are as follows
html script
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 col-sm-7 col-md-7 col-lg-4">
<page-title [title]=pageTitle></page-title>
</div>
</div>
<div class="row">
<div class="col-12">
<data-table [dataTable]="dataTable" ></data-table>
</div>
</div>
ts script
export class UploadsComponent implements OnInit {
pageTitle = '<i class="fa fa-table fa-fw"></i>Uploads';
dataTable: DataTable;
constructor() {
}
ngOnInit() {
this.initDataTable();
}
initDataTable() {
let startDateHeader = new DataTableHeader();
startDateHeader.text = 'Start Date';
startDateHeader.dataType = DataTableColumnDataType.DATE;
let uploadtypeHeader = new DataTableHeader();
uploadtypeHeader.text = 'Upload Type';
let statusHeader = new DataTableHeader();
statusHeader.text = 'Status';
let modifiedDateHeader = new DataTableHeader();
modifiedDateHeader.text = 'Modified Date';
modifiedDateHeader.dataType = DataTableColumnDataType.DATE;
let descriptionHeader = new DataTableHeader();
descriptionHeader.text = 'Description';
this.dataTable = new DataTable();
this.dataTable.headers = [startDateHeader, uploadtypeHeader, statusHeader, modifiedDateHeader, descriptionHeader];
}
}
and this is the output table i got
so can anyone give me some guidance on how to properly bind data to this table which comes in a JSON
I want to make an Album creator like on Facebook.
But when I save a picture to the firebase storage and I get back the URL to the picture I put in an object, but the view is not refreshing just if I add a new picture or move the cursor. How can I refresh the view automatically when I get the URL from firebase?
<div class="row pics">
<div class="col-md-4 col-xs-4">
<div class="newItem text-center">
<a (change)="addPicture($event)" class="newAlbum"><input type="file" id="file"/>Add Picture</a>
</div>
</div>
<div class="col-md-4 col-xs-4" *ngFor="let item of newAlbum.pictures; let i = index">
<div class="Item">
<img src="{{item?.pictureS}}" alt="" >
</div>
<textarea class="picture-desc" placeholder="Say something about this photo..." (keyup)="onKey($event,i)">{{item.desc}}</textarea>
</div>
</div>
Backend
readPicture() {
this.picItem = { pictureS: '', pictureF: this.file, desc: '' };
let that = this;
let uploadTask = this.data.albumUpload(this.newAlbum.dirname,this.file.name,this.file);
uploadTask.on('state_changed', function(snapshot) {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
}, function(error) {
}, function() {
that.picItem.pictureS = uploadTask.snapshot.downloadURL;
console.log(' NEW UPLOAD...');
that.newAlbum.pictures.push(that.picItem);
}
);
}
addPicture(e){
this.file = e.target.files[0];
this.readPicture();
}
onKey(e,i){
this.newAlbum.pictures[i].desc = e.target.value;
}
albumUpload(form: NgForm){
this.newAlbum.title = form.value.album_title;
this.newAlbum.desc = form.value.album_desc;
}
You could use NgZone to to trigger change detection when certain actions are done. You need to inject NgZone into the component. Once thats done you can use , run to update DOM.
constructor( public zone: NgZone){}
that.zone.run(() =>
that.newAlbum.pictures.push(that.picItem);
});
You can read more about ngZone here .
ps: I would advice you to use new arrow funcions to conserve this rather than using that.