HTTP Client, can't fetch data from API (cannot communicate between service and component)












0















I am trying to integrate the Angular HTTP Client to my simple angular CRUD App. I want the app to generate a picture of a dog (taken from an API) and to display it in the browser. Each picture has a set of properties (text, id, date, URL). Everything works fine except I cannot fetch the data (for the URL) from an API (it works if I provide the URL for the images as hard coded data, but I want dynamically added data from the API). The API does not require any authentication, it's free and the fetch request works, I think I am not doing something right. The API is this:



https://dog.ceo/dog-api/ 


and I am fetching from this link



https://dog.ceo/api/breeds/image/random;


The request returns an object with a status and a message (in the message is the link and I want to pass that link as an URL property to the image component). The Images' properties are being initialized through the form component.



I am using angular 7 and am on a windows 8 machine, I also have the following setup in my app:



-a service

-form component

-images component

-I also have an interface for the image which is imported in both form/image components and in the service but I can t seem to make it work.



//Here's my code so far:


//Interface (image.ts)

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


//Service (image-service.ts)

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {

images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}


//I have to modify this function but I can't seem to manage T__T
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

//Images component (images-component.ts)

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}

//Form component (img-form-component.ts)

import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;


constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

generateUrl() {
let url = this.imageService.getUrl();
return url;
}
}

/*Please let me know If I explained throughly enough the issue, and sorry if I seem clumsy and explaining, I am fairly new to Angular and am trying to learn it. This app is part of the process.
Thanks
*/


Thanks Chau Tran for your answer, however it's not working for my issue, or I am not implementing it right. Here's what I tried:



    //*function in the service*                                                                                                                            
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}

// * functions in the form component*
generateUrl() {
return this.imageService.getUrl().toPromise();
}
// *image instantiation*
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: await this.generateUrl()
}


If I try like this I get the following errors:



Module parse failed: The keyword 'yield' is reserved (33:21)
You may need an appropriate loader to handle this file type.
| text: this.text,
| date: new Date(),




            url: yield this.generateUrl()


| //this.generateUrl()
| };




'await' expression is only allowed within an async function



What does it mean 'appropriate loader to handle this file type' ? And what 'file type'? I m lost here. Also, can I make the onSubmit() in the constructor asynchronous? How would an implementation of that look (solving my url issue as well).

I also tried to modify the generateUrl() so as to subscribe to the getUrl() observable in the service but I am either not subscribing 'correctly' or something else is 'off':



I also tried like this (as if to subscribe to the observable getUrl() in the service, if I'm doing something wrong, someone please point it out, I can't figure it out myself:



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}

I put `url` = response[1] because that's how the response is formatted in the dog API. So, what am I doing wrongly, can someone please explain this to me ? Thanks


As Chau Tran suggested, I will include a bit more code maybe this can help someone figure what I can't on my own:



Here's my app.component.ts:



import { Component } from '@angular/core';



@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'dogapp';
}


Here's my package.json:



{
"name": "dogapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.1.0",
"@angular/common": "~7.1.0",
"@angular/compiler": "~7.1.0",
"@angular/core": "~7.1.0",
"@angular/forms": "~7.1.0",
"@angular/platform-browser": "~7.1.0",
"@angular/platform-browser-dynamic": "~7.1.0",
"@angular/router": "~7.1.0",
"bootstrap": "4.0.0-beta.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.11.0",
"@angular/cli": "~7.1.4",
"@angular/compiler-cli": "~7.1.0",
"@angular/language-service": "~7.1.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
}


Here's my image-service.ts:



   import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {
images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}

getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

Here's my images.component.ts:

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}


Here's my image-form.component.ts:



import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;
//imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});

//this.imageService.getUrl.subscribe(url => (this.url = url));
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
}

Here's my interface:

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


I cannot manage to make a get request to the dog API (https://dog.ceo/dog-api/) to fetch a URL and pass it as a property to each image, once it is being instantiated (data for each image instantiation is being initialized in the image-form component.ts). Thanks for any help =).









share|improve this question

























  • -I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

    – Dragos
    Jan 1 at 10:32
















0















I am trying to integrate the Angular HTTP Client to my simple angular CRUD App. I want the app to generate a picture of a dog (taken from an API) and to display it in the browser. Each picture has a set of properties (text, id, date, URL). Everything works fine except I cannot fetch the data (for the URL) from an API (it works if I provide the URL for the images as hard coded data, but I want dynamically added data from the API). The API does not require any authentication, it's free and the fetch request works, I think I am not doing something right. The API is this:



https://dog.ceo/dog-api/ 


and I am fetching from this link



https://dog.ceo/api/breeds/image/random;


The request returns an object with a status and a message (in the message is the link and I want to pass that link as an URL property to the image component). The Images' properties are being initialized through the form component.



I am using angular 7 and am on a windows 8 machine, I also have the following setup in my app:



-a service

-form component

-images component

-I also have an interface for the image which is imported in both form/image components and in the service but I can t seem to make it work.



//Here's my code so far:


//Interface (image.ts)

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


//Service (image-service.ts)

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {

images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}


//I have to modify this function but I can't seem to manage T__T
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

//Images component (images-component.ts)

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}

//Form component (img-form-component.ts)

import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;


constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

generateUrl() {
let url = this.imageService.getUrl();
return url;
}
}

/*Please let me know If I explained throughly enough the issue, and sorry if I seem clumsy and explaining, I am fairly new to Angular and am trying to learn it. This app is part of the process.
Thanks
*/


Thanks Chau Tran for your answer, however it's not working for my issue, or I am not implementing it right. Here's what I tried:



    //*function in the service*                                                                                                                            
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}

// * functions in the form component*
generateUrl() {
return this.imageService.getUrl().toPromise();
}
// *image instantiation*
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: await this.generateUrl()
}


If I try like this I get the following errors:



Module parse failed: The keyword 'yield' is reserved (33:21)
You may need an appropriate loader to handle this file type.
| text: this.text,
| date: new Date(),




            url: yield this.generateUrl()


| //this.generateUrl()
| };




'await' expression is only allowed within an async function



What does it mean 'appropriate loader to handle this file type' ? And what 'file type'? I m lost here. Also, can I make the onSubmit() in the constructor asynchronous? How would an implementation of that look (solving my url issue as well).

I also tried to modify the generateUrl() so as to subscribe to the getUrl() observable in the service but I am either not subscribing 'correctly' or something else is 'off':



I also tried like this (as if to subscribe to the observable getUrl() in the service, if I'm doing something wrong, someone please point it out, I can't figure it out myself:



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}

I put `url` = response[1] because that's how the response is formatted in the dog API. So, what am I doing wrongly, can someone please explain this to me ? Thanks


As Chau Tran suggested, I will include a bit more code maybe this can help someone figure what I can't on my own:



Here's my app.component.ts:



import { Component } from '@angular/core';



@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'dogapp';
}


Here's my package.json:



{
"name": "dogapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.1.0",
"@angular/common": "~7.1.0",
"@angular/compiler": "~7.1.0",
"@angular/core": "~7.1.0",
"@angular/forms": "~7.1.0",
"@angular/platform-browser": "~7.1.0",
"@angular/platform-browser-dynamic": "~7.1.0",
"@angular/router": "~7.1.0",
"bootstrap": "4.0.0-beta.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.11.0",
"@angular/cli": "~7.1.4",
"@angular/compiler-cli": "~7.1.0",
"@angular/language-service": "~7.1.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
}


Here's my image-service.ts:



   import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {
images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}

getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

Here's my images.component.ts:

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}


Here's my image-form.component.ts:



import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;
//imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});

//this.imageService.getUrl.subscribe(url => (this.url = url));
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
}

Here's my interface:

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


I cannot manage to make a get request to the dog API (https://dog.ceo/dog-api/) to fetch a URL and pass it as a property to each image, once it is being instantiated (data for each image instantiation is being initialized in the image-form component.ts). Thanks for any help =).









share|improve this question

























  • -I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

    – Dragos
    Jan 1 at 10:32














0












0








0








I am trying to integrate the Angular HTTP Client to my simple angular CRUD App. I want the app to generate a picture of a dog (taken from an API) and to display it in the browser. Each picture has a set of properties (text, id, date, URL). Everything works fine except I cannot fetch the data (for the URL) from an API (it works if I provide the URL for the images as hard coded data, but I want dynamically added data from the API). The API does not require any authentication, it's free and the fetch request works, I think I am not doing something right. The API is this:



https://dog.ceo/dog-api/ 


and I am fetching from this link



https://dog.ceo/api/breeds/image/random;


The request returns an object with a status and a message (in the message is the link and I want to pass that link as an URL property to the image component). The Images' properties are being initialized through the form component.



I am using angular 7 and am on a windows 8 machine, I also have the following setup in my app:



-a service

-form component

-images component

-I also have an interface for the image which is imported in both form/image components and in the service but I can t seem to make it work.



//Here's my code so far:


//Interface (image.ts)

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


//Service (image-service.ts)

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {

images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}


//I have to modify this function but I can't seem to manage T__T
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

//Images component (images-component.ts)

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}

//Form component (img-form-component.ts)

import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;


constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

generateUrl() {
let url = this.imageService.getUrl();
return url;
}
}

/*Please let me know If I explained throughly enough the issue, and sorry if I seem clumsy and explaining, I am fairly new to Angular and am trying to learn it. This app is part of the process.
Thanks
*/


Thanks Chau Tran for your answer, however it's not working for my issue, or I am not implementing it right. Here's what I tried:



    //*function in the service*                                                                                                                            
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}

// * functions in the form component*
generateUrl() {
return this.imageService.getUrl().toPromise();
}
// *image instantiation*
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: await this.generateUrl()
}


If I try like this I get the following errors:



Module parse failed: The keyword 'yield' is reserved (33:21)
You may need an appropriate loader to handle this file type.
| text: this.text,
| date: new Date(),




            url: yield this.generateUrl()


| //this.generateUrl()
| };




'await' expression is only allowed within an async function



What does it mean 'appropriate loader to handle this file type' ? And what 'file type'? I m lost here. Also, can I make the onSubmit() in the constructor asynchronous? How would an implementation of that look (solving my url issue as well).

I also tried to modify the generateUrl() so as to subscribe to the getUrl() observable in the service but I am either not subscribing 'correctly' or something else is 'off':



I also tried like this (as if to subscribe to the observable getUrl() in the service, if I'm doing something wrong, someone please point it out, I can't figure it out myself:



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}

I put `url` = response[1] because that's how the response is formatted in the dog API. So, what am I doing wrongly, can someone please explain this to me ? Thanks


As Chau Tran suggested, I will include a bit more code maybe this can help someone figure what I can't on my own:



Here's my app.component.ts:



import { Component } from '@angular/core';



@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'dogapp';
}


Here's my package.json:



{
"name": "dogapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.1.0",
"@angular/common": "~7.1.0",
"@angular/compiler": "~7.1.0",
"@angular/core": "~7.1.0",
"@angular/forms": "~7.1.0",
"@angular/platform-browser": "~7.1.0",
"@angular/platform-browser-dynamic": "~7.1.0",
"@angular/router": "~7.1.0",
"bootstrap": "4.0.0-beta.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.11.0",
"@angular/cli": "~7.1.4",
"@angular/compiler-cli": "~7.1.0",
"@angular/language-service": "~7.1.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
}


Here's my image-service.ts:



   import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {
images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}

getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

Here's my images.component.ts:

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}


Here's my image-form.component.ts:



import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;
//imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});

//this.imageService.getUrl.subscribe(url => (this.url = url));
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
}

Here's my interface:

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


I cannot manage to make a get request to the dog API (https://dog.ceo/dog-api/) to fetch a URL and pass it as a property to each image, once it is being instantiated (data for each image instantiation is being initialized in the image-form component.ts). Thanks for any help =).









share|improve this question
















I am trying to integrate the Angular HTTP Client to my simple angular CRUD App. I want the app to generate a picture of a dog (taken from an API) and to display it in the browser. Each picture has a set of properties (text, id, date, URL). Everything works fine except I cannot fetch the data (for the URL) from an API (it works if I provide the URL for the images as hard coded data, but I want dynamically added data from the API). The API does not require any authentication, it's free and the fetch request works, I think I am not doing something right. The API is this:



https://dog.ceo/dog-api/ 


and I am fetching from this link



https://dog.ceo/api/breeds/image/random;


The request returns an object with a status and a message (in the message is the link and I want to pass that link as an URL property to the image component). The Images' properties are being initialized through the form component.



I am using angular 7 and am on a windows 8 machine, I also have the following setup in my app:



-a service

-form component

-images component

-I also have an interface for the image which is imported in both form/image components and in the service but I can t seem to make it work.



//Here's my code so far:


//Interface (image.ts)

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


//Service (image-service.ts)

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {

images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}


//I have to modify this function but I can't seem to manage T__T
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

//Images component (images-component.ts)

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}

//Form component (img-form-component.ts)

import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;


constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

generateUrl() {
let url = this.imageService.getUrl();
return url;
}
}

/*Please let me know If I explained throughly enough the issue, and sorry if I seem clumsy and explaining, I am fairly new to Angular and am trying to learn it. This app is part of the process.
Thanks
*/


Thanks Chau Tran for your answer, however it's not working for my issue, or I am not implementing it right. Here's what I tried:



    //*function in the service*                                                                                                                            
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}

// * functions in the form component*
generateUrl() {
return this.imageService.getUrl().toPromise();
}
// *image instantiation*
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: await this.generateUrl()
}


If I try like this I get the following errors:



Module parse failed: The keyword 'yield' is reserved (33:21)
You may need an appropriate loader to handle this file type.
| text: this.text,
| date: new Date(),




            url: yield this.generateUrl()


| //this.generateUrl()
| };




'await' expression is only allowed within an async function



What does it mean 'appropriate loader to handle this file type' ? And what 'file type'? I m lost here. Also, can I make the onSubmit() in the constructor asynchronous? How would an implementation of that look (solving my url issue as well).

I also tried to modify the generateUrl() so as to subscribe to the getUrl() observable in the service but I am either not subscribing 'correctly' or something else is 'off':



I also tried like this (as if to subscribe to the observable getUrl() in the service, if I'm doing something wrong, someone please point it out, I can't figure it out myself:



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}

I put `url` = response[1] because that's how the response is formatted in the dog API. So, what am I doing wrongly, can someone please explain this to me ? Thanks


As Chau Tran suggested, I will include a bit more code maybe this can help someone figure what I can't on my own:



Here's my app.component.ts:



import { Component } from '@angular/core';



@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'dogapp';
}


Here's my package.json:



{
"name": "dogapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.1.0",
"@angular/common": "~7.1.0",
"@angular/compiler": "~7.1.0",
"@angular/core": "~7.1.0",
"@angular/forms": "~7.1.0",
"@angular/platform-browser": "~7.1.0",
"@angular/platform-browser-dynamic": "~7.1.0",
"@angular/router": "~7.1.0",
"bootstrap": "4.0.0-beta.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.11.0",
"@angular/cli": "~7.1.4",
"@angular/compiler-cli": "~7.1.0",
"@angular/language-service": "~7.1.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
}


Here's my image-service.ts:



   import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Injectable({
providedIn: "root"
})
export class ImageService {
images: Image;

private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});

selectedImage = this.imageSource.asObservable();

imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}

getImages(): Observable<Image> {
return of(this.images);
}

setFormImage(image: Image) {
this.imageSource.next(image);
}

addImage(image: Image) {
this.images.unshift(image);
}

getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}

Here's my images.component.ts:

import { Component, OnInit } from "@angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";

@Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image;

//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}

ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}

onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}


Here's my image-form.component.ts:



import { Component, OnInit } from "@angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "@angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "@angular/common/http";

@Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;

isNew: boolean = true;
//imageUrl: string = "https://dog.ceo/api/breeds/image/random";

constructor(private imageService: ImageService) {}

ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});

//this.imageService.getUrl.subscribe(url => (this.url = url));
}

onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};

//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}

generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}



generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
}

Here's my interface:

export interface Image {
id: string;
text: string;
url: string;
date: any;
}


I cannot manage to make a get request to the dog API (https://dog.ceo/dog-api/) to fetch a URL and pass it as a property to each image, once it is being instantiated (data for each image instantiation is being initialized in the image-form component.ts). Thanks for any help =).






service observable angular7 angular-httpclient






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 2 at 21:57







Dragos

















asked Jan 1 at 10:31









DragosDragos

113




113













  • -I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

    – Dragos
    Jan 1 at 10:32



















  • -I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

    – Dragos
    Jan 1 at 10:32

















-I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

– Dragos
Jan 1 at 10:32





-I call the getUrl() method from the service in generateUrl() in the form component and pass that as a param in the onSubmit() in the form component, but it does not work (I get a 404 status); So I think I am not performing the fetch request through the http client as I should. -The only way this works is if I define the url as hard-coded data either in getUrl() -in the service or in generateUrl() in the form component and return it, but I want the service to return a dynamically 'fetched' url from the dog API (dog.ceo/dog-api)

– Dragos
Jan 1 at 10:32












1 Answer
1






active

oldest

votes


















0














  generateUrl() {
let url = this.imageService.getUrl();
return url;
}


Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.



Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.



There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):



const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}





share|improve this answer
























  • Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

    – Dragos
    Jan 2 at 19:09











  • Can you update your question with more code?

    – Chau Tran
    Jan 2 at 21:46











  • I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

    – Dragos
    Jan 2 at 21:48











  • Try making your onSubmit() async by async onSubmit()

    – Chau Tran
    Jan 2 at 21:49











  • What you're trying in the last snippet in your question won't work, the code is still asynchronous.

    – Chau Tran
    Jan 2 at 21:50











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53994739%2fhttp-client-cant-fetch-data-from-api-cannot-communicate-between-service-and-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














  generateUrl() {
let url = this.imageService.getUrl();
return url;
}


Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.



Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.



There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):



const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}





share|improve this answer
























  • Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

    – Dragos
    Jan 2 at 19:09











  • Can you update your question with more code?

    – Chau Tran
    Jan 2 at 21:46











  • I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

    – Dragos
    Jan 2 at 21:48











  • Try making your onSubmit() async by async onSubmit()

    – Chau Tran
    Jan 2 at 21:49











  • What you're trying in the last snippet in your question won't work, the code is still asynchronous.

    – Chau Tran
    Jan 2 at 21:50
















0














  generateUrl() {
let url = this.imageService.getUrl();
return url;
}


Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.



Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.



There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):



const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}





share|improve this answer
























  • Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

    – Dragos
    Jan 2 at 19:09











  • Can you update your question with more code?

    – Chau Tran
    Jan 2 at 21:46











  • I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

    – Dragos
    Jan 2 at 21:48











  • Try making your onSubmit() async by async onSubmit()

    – Chau Tran
    Jan 2 at 21:49











  • What you're trying in the last snippet in your question won't work, the code is still asynchronous.

    – Chau Tran
    Jan 2 at 21:50














0












0








0







  generateUrl() {
let url = this.imageService.getUrl();
return url;
}


Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.



Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.



There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):



const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}





share|improve this answer













  generateUrl() {
let url = this.imageService.getUrl();
return url;
}


Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.



Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.



There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):



const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}






share|improve this answer












share|improve this answer



share|improve this answer










answered Jan 2 at 1:29









Chau TranChau Tran

1,3211422




1,3211422













  • Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

    – Dragos
    Jan 2 at 19:09











  • Can you update your question with more code?

    – Chau Tran
    Jan 2 at 21:46











  • I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

    – Dragos
    Jan 2 at 21:48











  • Try making your onSubmit() async by async onSubmit()

    – Chau Tran
    Jan 2 at 21:49











  • What you're trying in the last snippet in your question won't work, the code is still asynchronous.

    – Chau Tran
    Jan 2 at 21:50



















  • Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

    – Dragos
    Jan 2 at 19:09











  • Can you update your question with more code?

    – Chau Tran
    Jan 2 at 21:46











  • I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

    – Dragos
    Jan 2 at 21:48











  • Try making your onSubmit() async by async onSubmit()

    – Chau Tran
    Jan 2 at 21:49











  • What you're trying in the last snippet in your question won't work, the code is still asynchronous.

    – Chau Tran
    Jan 2 at 21:50

















Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

– Dragos
Jan 2 at 19:09





Hello, Your suggestion is not working or I am not implementing it correctly. Here's how I tried to implement your solution: getUrl(): Observable<any> { return this.http.get(this.imageUrl); }

– Dragos
Jan 2 at 19:09













Can you update your question with more code?

– Chau Tran
Jan 2 at 21:46





Can you update your question with more code?

– Chau Tran
Jan 2 at 21:46













I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

– Dragos
Jan 2 at 21:48





I think I included all the code in the 3 files but I will add more let me update my question. I will include more code without removing my initial explanations as I think this might help some1 figure it.

– Dragos
Jan 2 at 21:48













Try making your onSubmit() async by async onSubmit()

– Chau Tran
Jan 2 at 21:49





Try making your onSubmit() async by async onSubmit()

– Chau Tran
Jan 2 at 21:49













What you're trying in the last snippet in your question won't work, the code is still asynchronous.

– Chau Tran
Jan 2 at 21:50





What you're trying in the last snippet in your question won't work, the code is still asynchronous.

– Chau Tran
Jan 2 at 21:50




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53994739%2fhttp-client-cant-fetch-data-from-api-cannot-communicate-between-service-and-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

How to fix TextFormField cause rebuild widget in Flutter

Npm cannot find a required file even through it is in the searched directory