I have an angular 18 project which pulls the image data from an API to dispaly's it on screen. Can someone help me understand this behaviour?
The app throws an exception 'document is not defined' with the 'const a = document.createElement('a');' in the getImage(..) function of the ImageService.ts file. Even with this error, the app pulls the image data and draws it on the page.
When I take this line out, I get a different error and the image also does not show.
I added a breakpoint on the API and also the getImage function in ImageService.ts, when I refresh the page, the server API breakpoint is hit even before the breakpoints in the getImage function.
Is there a new way in which Observable are serviced or could it be an issue related to angular hooks?
ImageService.ts
@Injectable({
providedIn: 'root'
})
export class ImageService {
private apiUrl = 'http://localhost:5189/LoadImage';
constructor(private http: HttpClient) { }
getImage(objectId: string): Observable<Blob> {
const a = document.createElement('a');
return this.http.get(`${this.apiUrl}/${objectId}`, {responseType: 'blob' });
}
}
Imageponent.ts
@Component({
selector: 'app-image',
templateUrl: './imageponent.html',
styleUrls: ['./imageponent.css'],
standalone: true,
imports:[CommonModule]
})
export class ImageComponent implements OnInit {
imageUrl: any;
constructor(private imageService: ImageService) {}
async ngOnInit(): Promise<void> {
(await this.imageService.getImage('apple')).subscribe({
next: (blob) => {
const objectURL = URL.createObjectURL(blob);
this.imageUrl = objectURL;
}}
);
}
}
}
Imageponent.html:
<div>
<img [src]="imageUrl" alt="Image" />
</div>
Appponent.html
<app-image></app-image>
I have an angular 18 project which pulls the image data from an API to dispaly's it on screen. Can someone help me understand this behaviour?
The app throws an exception 'document is not defined' with the 'const a = document.createElement('a');' in the getImage(..) function of the ImageService.ts file. Even with this error, the app pulls the image data and draws it on the page.
When I take this line out, I get a different error and the image also does not show.
I added a breakpoint on the API and also the getImage function in ImageService.ts, when I refresh the page, the server API breakpoint is hit even before the breakpoints in the getImage function.
Is there a new way in which Observable are serviced or could it be an issue related to angular hooks?
ImageService.ts
@Injectable({
providedIn: 'root'
})
export class ImageService {
private apiUrl = 'http://localhost:5189/LoadImage';
constructor(private http: HttpClient) { }
getImage(objectId: string): Observable<Blob> {
const a = document.createElement('a');
return this.http.get(`${this.apiUrl}/${objectId}`, {responseType: 'blob' });
}
}
Imageponent.ts
@Component({
selector: 'app-image',
templateUrl: './imageponent.html',
styleUrls: ['./imageponent.css'],
standalone: true,
imports:[CommonModule]
})
export class ImageComponent implements OnInit {
imageUrl: any;
constructor(private imageService: ImageService) {}
async ngOnInit(): Promise<void> {
(await this.imageService.getImage('apple')).subscribe({
next: (blob) => {
const objectURL = URL.createObjectURL(blob);
this.imageUrl = objectURL;
}}
);
}
}
}
Imageponent.html:
<div>
<img [src]="imageUrl" alt="Image" />
</div>
Appponent.html
<app-image></app-image>
Share
Improve this question
edited Nov 21, 2024 at 4:36
Naren Murali
58.9k5 gold badges44 silver badges77 bronze badges
asked Nov 20, 2024 at 21:55
Bijaya RaiBijaya Rai
559 bronze badges
2
- why not just set imageURL to: localhost:5189/LoadImage/apple ? (Just return a file from backend... ) I can't explain the errors btw... it doesn't seem like you use const a... so it's odd. As is you're just slowing things down by having javascript create a copy. – browsermator Commented Nov 20, 2024 at 23:05
- 1 You should disable SSR if you don't need it. – Matthieu Riegler Commented Nov 20, 2024 at 23:58
1 Answer
Reset to default 1On the server, document
and window
objects do not exist as in the browser, hence certain functionality involving these objects won't work.
The createObjectUrl seems to be tied to the document
and window
object hence the issue is happening.
To solve this, you have to ensure this logic executes only on the browser.
We have two tools for this.
We can use defer
when we want to render HTML only on the browser.
We can use afterNextRender
when we want to run component code only on the browser.
So you can update your code to.
@Component({
selector: 'app-image',
templateUrl: './imageponent.html',
styleUrls: ['./imageponent.css'],
standalone: true,
imports: [CommonModule],
})
export class ImageComponent implements OnInit {
imageUrl: any = 'https://placehold.co/600x400'; // show some loading image here!
constructor(private imageService: ImageService) {
afterNextRender(() => this.loadImage()); // runs only on browser
}
loadImage() {
this.imageService.getImage('apple').subscribe({
next: (blob: Blob) => {
const objectURL = URL.createObjectURL(blob);
this.imageUrl = objectURL;
},
});
}
}