angular2 display base64 image - image

I'm trying to display an image that comes from a server as a base64 string. The http and server details are not important to my question (basically, it works).
I have this code that does not work; i.e. I see all the data in the component but no image shows on the screen.
the service:
import { Injectable } from 'angular2/core'
import {Observable} from 'rxjs/Observable';
#Injectable()
export class ImageService {
constructor() {
}
public getImage(): Observable<string> {
return Observable.create(imageData);
}
}
const imageData: string = "iVBORw0KGgoAAAANSUhE...";
The component:
import { Component } from 'angular2/core';
import { ImageService } from './service'
#Component({
selector: 'my-app',
template: '<div><img [src]="data:image/PNG;base64,{{imageContent}}"> </div>',
providers: [ImageService]
})
export class AppComponent {
private imageContent: string = "";
constructor(imageService: ImageService) {
imageService.getImage().subscribe(response => {
this.imageContent = response;
});
}
}
As mentioned, the code does not work. Instead of the image on the screen, I receive: Quotes are not supported for evaluation!
I'll appreciate a working example for this simple problem.

The following example shows how to display base64 encoded images using ASP.NET Core and Angular 2:
PhotoController (server-side)
[HttpGet]
public async Task<IEnumerable<PhotoResource>> GetPhotos(int entityId)
{
// get photo information
// in my case only path to some server location is stored, so photos must be read from disk and base64 encoded
foreach (var photoR in photoResources)
{
var currPath = Path.Combine(Host.ContentRootPath, "upload", photoR.FileName);
byte[] bytes = System.IO.File.ReadAllBytes(currPath);
photoR.Content = Convert.ToBase64String(bytes);
}
return photoResources;
}
PhotoService (Angular service)
getPhotos(int entityId) {
return this.http.get(`${this.apiBasePath}vehicles/${vehicleId}/photos`)
.map(res => res.json());
}
entity-component.ts
Server already sends encoded content, so I am just preparing a property containing header and content. Html template is separate.
this.photoService.getPhotos(this.entityId)
.subscribe(photos => {
this.photos = photos;
for (let photo of this.photos) {
photo.imageData = 'data:image/png;base64,' + photo.content;
}
});
entity-component.html
<img *ngFor="let photo of photos" [src]="photo.imageData" class="img-thumbnail">

Related

Angular, error 500 after sending the request in the header

I have a hard time passing the right angular request to the header. This is my service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpHeaders }
from '#angular/common/http';
import { Utente } from '../model/Utente ';
import { Prodotto } from '../model/Prodotto ';
import { OktaAuthService } from '#okta/okta-angular';
import { Observable, from } from 'rxjs';
import { Carrello } from '../model/Carrello ';
import { userInfo } from 'node:os';
import { getLocaleCurrencyCode } from '#angular/common';
const headers = new HttpHeaders().set('Accept', 'application/json');
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
constructor(
private httpClient:HttpClient, private oktaAuth:OktaAuthService ) {}
getCarr(){
return this.httpClient.get<Carrello[]>('http://localhost:8080/prodotti/utente/vedicarrelloo', {headers} );
}
}
This is my spring method:
#Transactional(readOnly = true)
public List<Carrello> getCarrello(#AuthenticationPrincipal OidcUser utente){
Utente u= utenteRepository.findByEmail(utente.getEmail());
return carrelloRepository.findByUtente(u);
}
In console I get this error (error 500):
https://i.stack.imgur.com/BiONS.png
this error corresponds in my console to "java.lang.NullPointerException: null.
But if I access localhost: 8080, I can see the answer correctly, so I assume there is a problem in passing the request header in angular, can anyone tell me where am I wrong, please? I specify that I get this error only in the methods where the OidcUser is present, the rest works perfectly. Thank you!
You need to send an access token with your request. Like this:
import { Component, OnInit } from '#angular/core';
import { OktaAuthService } from '#okta/okta-angular';
import { HttpClient } from '#angular/common/http';
import sampleConfig from '../app.config';
interface Message {
date: string;
text: string;
}
#Component({
selector: 'app-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
failed: Boolean;
messages: Array<Message> [];
constructor(public oktaAuth: OktaAuthService, private http: HttpClient) {
this.messages = [];
}
async ngOnInit() {
const accessToken = await this.oktaAuth.getAccessToken();
this.http.get(sampleConfig.resourceServer.messagesUrl, {
headers: {
Authorization: 'Bearer ' + accessToken,
}
}).subscribe((data: any) => {
let index = 1;
const messages = data.messages.map((message) => {
const date = new Date(message.date);
const day = date.toLocaleDateString();
const time = date.toLocaleTimeString();
return {
date: `${day} ${time}`,
text: message.text,
index: index++
};
});
[].push.apply(this.messages, messages);
}, (err) => {
console.error(err);
this.failed = true;
});
}
}
On the Spring side, if you want it to accept a JWT, you'll need to change to use Jwt instead of OidcUser. Example here.
#GetMapping("/")
public String index(#AuthenticationPrincipal Jwt jwt) {
return String.format("Hello, %s!", jwt.getSubject());
}

How do I pass data back to previous page using `navigateBack`?

I am using navigateTo to open a page with listview and would like to pass the results back using navigateBack but unable to achieve that. Any idea?
With Service class and Observable, you can achieve this.
notify.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs-compat/Subject';
#Injectable({
providedIn: 'root'
})
export class NotifyService {
private refreshDataForView = new Subject<any>();
refreshDataForParentViewObservable$ = this.refreshDataForView.asObservable();
public relaodDataForParentView(data: any) {
if (data) {
this.refreshDataForView.next(data);
}
}
}
Second componenet.ts
constructor(
private notifyService: NotifyService
) { }
goBack() {
this.notifyService.relaodDataForParentView({ data: 'any data you wanrt to pass here ' });
this.router.back();
}
First component.ts
reloadDataSubscription: any;
constructor(
private notifyService: NotifyService
) {}
ngOnInit() {
this.reloadDataSubscription = this.notifyService.refreshDataForParentViewObservable$
.subscribe((res) => {
console.log('======', res);
// do what you want to do with the data passed from second view
});
}

Angular UI not updating after data changes

Im new to the MEAN stack and having trouble with getting data changes to be refreshed in the UI. I know the data is getting saved properly in MongoDB, and also retrieved, because when I create a Todo item and I refresh the page, the newly added Todo item appears in the Todo List. The problem is that it isnt happening dynamically.
I've tried a number of different things including NgZone and ChangeDetectorRef to detect changes, not sure what I'm doing wrong..
Let me know if any more info is needed.. thank you!
The Todo List component:
import { Component, Input, OnInit, NgZone } from '#angular/core';
import { Todo } from '../todo.model';
import { TodoService } from '../../todo.service';
#Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.scss'],
providers: [TodoService]
})
export class TodoListComponent implements OnInit {
#Input() todos: Todo[] = [];
constructor(private _todoService: TodoService, private zone: NgZone){}
getTodos() {
console.log('todo list - get todos');
this._todoService.getTodos()
.subscribe(resTodoData => {
this.zone.run(() => {
this.todos = resTodoData;
});
});
}
ngOnInit() {
console.log('todo list - init');
this.getTodos();
}
}
Service Component:
import { Injectable, NgZone } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from
'#angular/http';
import { map } from 'rxjs/operators';
import { Todo } from './todos/todo.model';
#Injectable({
providedIn: 'root'
})
export class TodoService {
// these were configured in express server
private _getUrl = "/api/todos";
private _postUrl = "/api/todo";
constructor(private _http: Http, private zone: NgZone) { }
getTodos() {
let json = this._http.get(this._getUrl)
.pipe(map((response: Response) => response.json()));
return json;
}
addTodo(todo: Todo) {
let headers = new Headers({ 'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers });
return this._http.post(this._postUrl, JSON.stringify(todo),
options)
.pipe(map((response: Response) => response.json()));
}
}

Could not find acceptable representation for file download

I want to implement file download using this Angular 6 code:
Rest API:
private static final Logger LOG = LoggerFactory.getLogger(DownloadsController.class);
private static final String EXTERNAL_FILE_PATH = "/Users/test/Documents/blacklist_api.pdf";
#GetMapping("export")
public ResponseEntity<FileInputStream> export() throws IOException {
File pdfFile = Paths.get(EXTERNAL_FILE_PATH).toFile();
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(pdfFile.length())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new FileInputStream(pdfFile));
}
Service:
import {Injectable} from '#angular/core';
import {HttpClient, HttpParams} from "#angular/common/http";
import {Observable} from "rxjs/index";
import {environment} from "../../../environments/environment";
import {HttpUtils} from "../common/http-utils";
import { map } from 'rxjs/operators';
import {Http, ResponseContentType} from '#angular/http';
#Injectable({
providedIn: 'root'
})
export class DownloadService {
constructor(private http: HttpClient) {
}
downloadPDF(): any {
return this.http.get(environment.api.urls.downloads.getPdf, {
responseType: 'blob'
})
.pipe(
map((res: any) => {
return new Blob([res.blob()], {
type: 'application/pdf'
})
})
);
}
}
Component:
import {Component, OnInit} from '#angular/core';
import {DownloadService} from "../service/download.service";
import {ActivatedRoute, Router} from "#angular/router";
import {flatMap} from "rxjs/internal/operators";
import {of} from "rxjs/index";
import { map } from 'rxjs/operators';
#Component({
selector: 'app-download',
templateUrl: './download.component.html',
styleUrls: ['./download.component.scss']
})
export class DownloadComponent implements OnInit {
constructor(private downloadService: DownloadService,
private router: Router,
private route: ActivatedRoute) {
}
ngOnInit() {
}
export() {
this.downloadService.downloadPDF().subscribe(res => {
const fileURL = URL.createObjectURL(res);
window.open(fileURL, '_blank');
});
}
}
The file is present in the directory but when I try to download it I get error:
18:35:25,032 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-2) Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
Do you know how I can fix this issue?
Do I need to add additional configuration in order to download the file via Angular web UI?
I use spring-boot-starter-parent version 2.1.0.RELEASE
FileSaver npmjs.com/package/ngx-filesaver is the best library for file download in Angular6 but it has various issues in ios devices. We fixed it by writing own methods and conditionally handling it .
Component
download() {
this.downloadService.downloadPDF().subscribe(async (res: Blob) => {
if (this.isIOSMobileDevice) {
const file = new File([res], fileName, { type: 'application/pdf' });
const dataStringURL: any = await this.fileService.readFile(file);
this.hrefLink = this.sanitizer.bypassSecurityTrustUrl(dataStringURL);
} else {
saveFile(res, fileName);
}
});
}
export const saveFile = (blobContent: Blob, fileName) => {
const isIOS = (!!navigator.platform.match(/iPhone|iPod|iPad/)) || (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2);
const blob = new Blob([blobContent], { type: 'application/pdf' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
document.body.appendChild(link);
link.href = url;
link.target = '_self';
link.download = fileName;
link.click();
document.body.removeChild(link);
}
};
File Service
async readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
HTML code
<a *ngIf="isIOSMobileDevice" [href]="hrefLink"
target="_blank">Download</a>
<a *ngIf="!isIOSMobileDevice" href="javascript:;" (click)="download"
target="_blank">Download</a>
For ios Mobile devices ,download method has to be called in prerequisite so that we get hrefLink.

NativeScript one-way databinding doesn't update view

This seems like a pretty simple case to me, but I'm obviously missing something. I have a Model to be bound to the View. I then load the Model with an Http call. Why doesn't the View update? I thought that was the whole point of one-way binding.
I have verified that I'm getting back the data I'm expecting from the http call.
Update
I added a button to the screen and databinding will actually update the screen with the http loaded data for both fields on button push, even though the button method only sets one of the values. So either there's a bug in NativeScript or I'm not doing something incorrectly.
Update 2 Just the act of clicking the button will trigger the binding to happen. I've modified the code to have an empty tap handler, and just clicking the button makes it bind.
typescript
import { Component, ChangeDetectionStrategy, OnInit } from "#angular/core";
import { Job } from "../../shared/customer/job";
import { Http, Headers, Response } from "#angular/http";
import { Observable } from "rxjs/Rx";
#Component({
selector: "my-app",
templateUrl: "pages/job-details/job-details.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class JobDetailsComponent implements OnInit {
job: Job;
salesAssociateName: string = "x";
constructor(private http: Http) {
this.job = new Job();
}
ngOnInit() {
this.getJob(1234);
}
getJob(leadId: number) {
var url = "https://url-not-for-you/job?franchiseeid=48&leadid=" + leadId;
var headers = this.createRequestHeader();
this.http.get(url, { headers: headers }).map(response => response.json())
.do(data => this.setData(data[0]))
.subscribe(
() => this.success(),
(error) => this.error()
);
}
private createRequestHeader() {
let headers = new Headers();
headers.append("AuthKey","blah");
headers.append("AuthToken", "blee");
return headers;
}
setData(job) {
this.job.FullName = job["FullName"];
this.job.SalesAssociateName = job["SalesAssociateName"];
this.salesAssociateName = this.job.SalesAssociateName;
console.log("Found job for customer: " + job["FullName"]);
}
success() {
// nothing useful
}
error() {
alert("There was a problem retrieving your customer job.");
}
changeSA() {
}
}
html
<StackLayout>
<Label [text]="job.FullName"></Label>
<Label [text]="salesAssociateName"></Label>
<Button text="Push" (tap)="changeSA()"></Button>
</StackLayout>
Your code will work as expected with the default ChangeDetectionStrategy. however, you have changed the strategy to onPush
In order to make your binding work as expected in the default changeStrategy delete the following line
changeDetection: ChangeDetectionStrategy.OnPush
or change it to
changeDetection: ChangeDetectionStrategy.Default
More about the Angular-2 ChangeDetectionStrategy here and here
If you still want to use onPush instead of the default strategy then your properties should be declared as #input() and once the change is made (in your case in setData) marked with markForCheck()
The reason your binding is working when triggered from Button tap is because
application state change can be triggered by:
Events - tap, swipe,
XHR - Fetching data from a remote server
Timers - e.g. setTimeout()
For testing purposes and if someone is interested of how to implement the scenario with onPush here is a sample code:
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, NgZone, Input } from "#angular/core";
import { Http, Headers, Response } from "#angular/http";
import { Observable as RxObservable } from "rxjs/Rx";
import "rxjs/add/operator/map";
import "rxjs/add/operator/do";
#Component({
selector: "my-app",
templateUrl: "app.component.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
#Input() public job: any = { salesAssociateName: "default job" };
#Input() public salesAssociateName: string = "default name";
constructor(private http: Http, private change:ChangeDetectorRef) { }
ngOnInit() {
this.getJob();
}
getJob() {
var url = "http://httpbin.org/get";
var headers = this.createRequestHeader();
this.http.get(url, { headers: headers })
.map(response => response.json())
.do(data => {
this.setData();
}).subscribe(
() => this.success(),
(error) => this.error()
);
}
private createRequestHeader() {
let headers = new Headers();
return headers;
}
setData() {
this.job.salesAssociateName = "NEW job SalesAssociateName";
this.salesAssociateName = "NEW job FullName";
this.change.markForCheck();
}
success() {
alert("success");
}
error() {
alert("There was a problem retrieving your customer job.");
}
}

Resources