Передача данных из службы в компонент --> дочерние компоненты

Чтобы быть очень кратким, я использую этот Plunker. У меня есть сценарий, в котором я должен создать управляет динамически, считывая данные элементов из службы. Поэтому, когда я читаю данные из службы, они асинхронны. Но мне нужно создать некоторые конкретные объекты на основе данных, которые я получаю от службы, и передать их дочерним компонентам. Итак, вот моя логика

Html для основного компонента показан ниже.

   <ion-content padding class="container" *ngIf="questions"> 

   <app-dynamic-form [questions]="questions"></app-dynamic-form>

   </ion-content>

Класс для основного компонента ниже

Class ComponentMain{

   @Input() questions: QuestionBase<any>[]=[];
  constructor(public navCtrl: NavController, public navParams: NavParams,private qcs: Service)
    {
      qcs.getQuestions(this.parameter1.$key).subscribe(result => this.questions = result);
    }

}

HTML-код дочерних компонентов выглядит следующим образом.

<div *ngIf="form">
  <form (ngSubmit)="onSubmit()" [formGroup]="form">

    <div *ngFor="let question of questions" class="form-row">
      <div *ngIf="question">
        <app-question [question]="question" [form]="form"></app-question>
      </div>
   </div>
</form>
</div>

Дочерний компонент выглядит следующим образом

Class ChildComponent implements AfterViewInit {

  @Input() questions: QuestionBase<any>[] = [];

 Constructor(){

  }

 ngAfterViewInit(){

 this.form = this.qcs.toFormGroup(this.questions);

 }

}

Существует второй дочерний компонент, который зависит от дочернего компонента для создания элементов управления. Таким образом, элементы управления заполняются только в ngOnit второго дочернего компонента, из-за чего элементы управления не создаются. Я пытался использовать множество хуков жизненного цикла, таких как OnInit, OnChanges и т. д. Но ни один из них не дал мне результата. Я уверен, что упускаю что-то, что я не могу понять.

Class Service(){

questions: QuestionsData<any>[]=[];

getQuestions(FormKey: string) {

var dbQuestions = this.af.list('/elements', {
   query: {
   limitToLast: 200,
   orderByChild: 'formid',
   equalTo: FormKey
  }
})

  dbQuestions.subscribe(snapshots=>{
  snapshots.forEach(elementData => {
  this.questions.push(new TextboxQuestion({
        key: elementData.elementname,
        label: elementData.displaytext,
        value: elementData.elementvalue,
        required: false,
        order: elementData.sortorder
      }))
  }
 }
}

person DEV    schedule 21.01.2018    source источник
comment
По сути, вам нужна помощь в реализации строк TODO здесь Angular.io — Dynamic Forms?   -  person Richard Matsen    schedule 22.01.2018
comment
Пара вопросов к вам: 1. Почему вы назначили декоратор ввода на вопросы в основном компоненте? Из того, что я видел, в MainComponent ставится под сомнение свойство компонента, а не ввод из какого-либо другого компонента. 2. Вы подписались на свой getQuestions() в своем сервисе, но я не вижу никакого объявления или возврата, указывающего на то, что getQuestions() является наблюдаемым? 3. Какую ошибку вы получаете, или ожидаемое поведение против фактического поведения?   -  person Chau Tran    schedule 22.01.2018
comment
@RichardMatsen Да, точно. Я хотел бы использовать именно этот пример, но с сервисом api.myjson.com/bins/o0aex с этим json. Поэтому, когда я вызываю службу, поскольку все асинхронно, я должен подписаться и создать наблюдаемый вопрос, чтобы используемые компоненты получали данные из службы..... Но я получаю неопределенное при создании элементов из тот же пример. Не могли бы вы помочь мне с тем же примером, но с сервисом. Спасибо.   -  person DEV    schedule 22.01.2018
comment
Мне удалось заставить его работать примерно с 3 или 4 настройками. Выложу в ближайшее время.   -  person Richard Matsen    schedule 22.01.2018


Ответы (2)


Глядя на этот пример Angular.io — Dynamic Forms, он по существу создает форму из метаданных во время выполнения.

Есть пара комментариев, указывающих на то, что пример не совсем закончен.

@Injectable()
export class QuestionService {

  // Todo: get from a remote source of question metadata
  // Todo: make asynchronous
  getQuestions() {
  ...

Это шаги, которые я предпринял, чтобы закончить его и очистить сообщения об ошибках.


вопрос.service.ts

Изменено getQuestions для асинхронного возврата вопросов.

Injectable()
export class QuestionService {

  constructor(
    private http: Http
  ) {}

  getQuestions$() {
    const url = 'https://api.myjson.com/bins/d0srd';
    return this.http.get(url)
      .map(response => response.json())
      .map(questionMetadata => this.metadataToQuestions(questionMetadata))
      .map(questions => questions.sort((a, b) => a.order - b.order))
  }

  private metadataToQuestions(questionMetadata) {
    return questionMetadata.questions.map(this.toQuestion)
  }

  private toQuestion(elementData) {
    // expand for additional control types
    return new TextboxQuestion({
      key: elementData.elementname,
      label: elementData.displaytext,
      value: elementData.elementvalue,
      required: false,
      order: elementData.sortorder
    })
  }
}


app.component.ts

Изменен тип переменной questions на наблюдаемый, добавлен асинхронный канал в шаблон.

@Component({
  ...
  template: `
    <div>
      <h2>Job Application for Heroes</h2>
      <app-dynamic-form [questions]="(questions$ | async)"></app-dynamic-form>
    </div>
  `,
  ...
})
export class AppComponent implements OnInit {

  questions$: Observable<any>;

  constructor(
    private questionService: QuestionService
  ) {}

  ngOnInit() {
    this.questions$ = this.questionService.getQuestions$();
  }
}


динамическая-form.component.ts

Переменная @Input questions изменена на стиль установки/получения для обработки начального нулевого значения.
Изменен хук, в котором создается форма, с ngOnInit на ngOnChanges для обработки асинхронного поступления вопросов.

export class DynamicFormComponent implements OnChanges {

  private _questions = [];
  @Input() 
  set questions(value: any[]) {
    this._questions = value || [];
  }
  get questions(): any[] {
    return this._questions;
  }

  ...

  ngOnChanges() {
    this.form = this.qcs.toFormGroup(this.questions);
  }

}


динамическая форма-question.component.ts

Добавьте дополнительную проверку в геттер isValid, чтобы убедиться, что проверяемый элемент управления существует.

export class DynamicFormQuestionComponent {
  ...
  get isValid() { return this.form.controls[this.question.key] 
    ? this.form.controls[this.question.key].valid : true; }
}
person Richard Matsen    schedule 22.01.2018
comment
вы ответили на мой вопрос, но у меня есть одно отличие: у меня есть база данных firebase. Итак, я получаю данные в виде наблюдаемых. Итак, я пытаюсь выяснить, как сопоставить это наблюдаемое значение с функциями, которые вы объяснили. Спасибо за пример кода. Ценить это. - person DEV; 23.01.2018
comment
Вы можете видеть, что у question.service есть http.get, который возвращает наблюдаемое. Просто подставьте туда свой вызов firebase. - person Richard Matsen; 23.01.2018
comment
Я сделал это, но выдает ошибку, говоря, что ключ не найден. Как только я освобожусь, я посмотрю и дам вам знать, что я сделал, чтобы заставить его работать. - person DEV; 23.01.2018
comment
В этом нет большой хитрости, вам просто нужно поменять http-вызов на вызов firebase. - person Richard Matsen; 23.01.2018
comment
Я сделал точно так же, он говорит, что ключ не найден. - person DEV; 23.01.2018
comment
Недостаточно информации, но это просто звучит как плохой запрос. - person Richard Matsen; 23.01.2018
comment
Давайте продолжим это обсуждение в чате. - person DEV; 23.01.2018

Я не уверен, что понял вашу проблему, но когда мне нужно передать данные из службы в компонент, я использую подписку таким образом.

Два компонента (родительский компонент и его дочерние элементы или другой другой компонент) могут совместно использовать службу, интерфейс которой обеспечивает двустороннюю связь.

Как и в шаблоне Observer, в этом случае областью действия экземпляра службы является уведомление от компонента (издателя) и других компонентов (подписчика).

mycomponent.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MyComponentService{
    // Observable 
    private sampleObservable = new Subject<boolean>();
    // Observable boolean streams
    sampleSubscriber = this.sampleObservable.asObservable();
    // Event for notification from publisher to subscriber
    sampleEventChanged(value:boolean)
    {
        this.sampleObservable.next();
    }
}

В компоненте, который хочет уведомить всех подписчиков об изменении своего состояния:

mycomponent-publisher.ts

import { Component } from '@angular/core';
import { MyService } from './mycomponent.service';

@Component({
  selector: 'app-my-control-publisher',
  template: `
  <h2>This is the publisher control</h2>
  <button (click)="announce()">Announce to subscriber</button>
  `,
  providers: [MyService]
})

export class MyControlPublisherComponent 
{
  constructor(private myService: MyService) { }

  announce() 
  {
    this.myService.sampleEventChanged(true);
  }
}

В компоненте подписчика, который хочет получить уведомление.

mycomponent-subscriber.ts

import { Component, OnDestroy } from '@angular/core';
import { MyService } from './mycomponent.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
 selector: 'app-my-control-subscriber',
 template: `
 <h2>This is the subscriber control</h2>
 `,
})

export class MyControlSubscriberComponent 
{
  // Subscriptions
  private componentSubscription: Subscription;

  constructor(private myService: MyService) 
  {
    // Subscription of the notifications
    this.componentSubscription= this.myService.sampleSubscriber.subscribe(value =>
    {
      // Put the code for manage the notification here
    }
  }

  ngOnDestroy()
  {
    // Release subscription to avoid memory leaks when the component is destroyed
    this.componentSubscription.unsubscribe();
  }
}

Я надеюсь, что это может помочь вам.

person Marco Barbero    schedule 22.01.2018
comment
Ричард, ответь на мой вопрос. Спасибо за ваш ответ в любом случае. - person DEV; 23.01.2018