HTTP-клиент, не может получить данные из API (не может связаться между службой и компонентом) - PullRequest
0 голосов
/ 01 января 2019

Я пытаюсь интегрировать Angular HTTP Client в мое простое угловое приложение CRUD.Я хочу, чтобы приложение генерировало изображение собаки (взято из API) и отображало его в браузере.Каждое изображение имеет набор свойств (текст, идентификатор, дата, URL).Все работает отлично, за исключением того, что я не могу получить данные (для URL) из API (это работает, если я предоставляю URL для изображений в виде жестко закодированных данных, но я хочу динамически добавлять данные из API).API не требует никакой аутентификации, он бесплатный и запрос на выборку работает, я думаю, что я что-то не так делаю.API это:

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

и я получаю эту ссылку

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

Запрос возвращает объект со статусом и сообщением (в сообщении есть ссылка и яхотите передать эту ссылку как свойство URL компоненту изображения).Свойства изображений инициализируются с помощью компонента формы.

Я использую Angular 7 и работаю на компьютере с Windows 8, в моем приложении также есть следующие настройки:

-a служба
-форма компонента
-изображения компонента
-У меня также есть интерфейс для изображения, которое импортируется как в компоненты формы / изображения, так и в сервис, но я не могу заставить его работать.

//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
*/

Спасибо Чау Трану за ваш ответ, однако он не работает для моей проблемы, или я неправильно его реализую.Вот что я попробовал:

    //*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()
     }

Если я попробую вот так, я получу следующие ошибки:

Ошибка синтаксического анализа модуля: зарезервировано ключевое слово yield (33:21)соответствующий загрузчик для обработки этого типа файлов.|текст: этот.текст, |дата: новая дата (),

            url: yield this.generateUrl()

|//this.generateUrl () |};

Выражение 'await' разрешено только внутри функции async

Что означает «соответствующий загрузчик для обработки этого типа файлов»?И что такое «тип файла»?Я потерялся здесь.Кроме того, я могу сделать onSubmit() в конструкторе асинхронным?Как бы это реализовать (решая и мою проблему url).
Я также пытался изменить generateUrl(), чтобы подписаться на getUrl(), наблюдаемый в сервисе, но я либо не подписываюсь 'правильно 'или что-то еще' выключено ':

Я тоже так пытался (как если бы подписался на наблюдаемое getUrl() в сервисе, если я что-то делаю не так, кто-то, пожалуйста, укажите это,Я не могу понять это сам:

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

Как предложил Чау Тран, я добавлю немного больше кода, может быть, это может помочь кому-то понять, что я не могу самостоятельно:

Вот мой app.component.ts:

import {Component} из '@ angular / core';

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

Вот мой 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"
  }
}

Вот мой 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);
  }
}

Вот мой 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 =).

1 Ответ

0 голосов
/ 02 января 2019
  generateUrl() {
     let url = this.imageService.getUrl();
     return url;
  }

Ваш imageService.getUrl возвращает Observable, но вы пытаетесь присвоить его свойству url в newImage, которое, как я полагаю, url является string.

Измените ваш getUrl, чтобы он возвращал url, или убедитесь, что вы обрабатываете асинхронные данные в generateUrl() перед возвратом URL.

Есть несколько способов, но в вашем случае я упомяну один, который работает сразу (но он довольно грязный):

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