2016-12-15 5 views
16

У меня есть родительский компонент, который получает данные с сервера через службу. Мне нужно, чтобы некоторые данные передавались компоненту Child.Угловой 2 - Передача данных дочернему компоненту после инициализации родителя

Я пытался передать эти данные обычным @Input(), однако, как я и ожидал, данные, которые я получаю в компоненте Child, составляют undefined.

Пример

Родитель Компонент

@Component({ 
    selector: 'test-parent', 
    template: ` 
     <test-child [childData]="data"></test-child> 
    ` 
}) 

export class ParentComponent implements OnInit { 
    data: any; 

    ngOnInit() { 
     this.getData(); 
    } 

    getData() { 
     // calling service and getting data 
     this.testService.getData() 
      .subscribe(res => { 
       this.data = res; 
      }); 
    } 
} 

Детский компонент

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    @Input() childData: any; 

    ngOnInit() { 
     console.log(childData); // logs undefined 
    } 
} 

я сохранил пример простой для того, чтобы показать, что я пытаюсь делать. Мой вопрос заключается в том, можно ли передавать данные ребенку после инициализации. Я не уверен, но в этом случае поможет двусторонняя привязка данных?

Подробнее Исследование

После посланных ответов я попытался с помощью ngOnChanges жизненный цикл крюк. Проблема, с которой я сталкиваюсь, заключается в том, что функция ngOnChanges не запускается, когда данные обновляются. Данные являются объектом, поэтому я думаю, что изменения не обнаруживаются.

Обновленный код в дочерних компонентах

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnChanges { 
    @Input() childData: any; 

    ngOnChanges() { 
     console.log(childData); // logs undefined 
    } 
} 

Я попытался тест с живыми примерами на Plunker от угловой команды демонстрирующего Lifecycle Крючков. В тесте я обновил OnChangesComponent для передачи объекта, который, как видно из примера при обновлении объекта, ngOnChanges не обнаруживает изменения. (Это также показано в этом question)

https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV

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

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

Проводка больше коды, чтобы помочь объяснить лучше

родительского компонент

@Component({ 
    selector: 'test-parent', 
    template: ` 
     <test-child [childType]="numbers" [childData]="data" (pickItem)="pickNumber($event)"> 
      <template let-number> 
       <span class="number" [class.is-picked]="number.isPicked"> 
        {{ number.number }} 
       </span> 
      </template> 
     </test-child> 
     <test-child [childType]="letters" [childData]="data" (pickItem)="pickLetter($event)"> 
      <template let-letter> 
       <span class="letter" [class.is-picked]="letter.isPicked"> 
        {{ letter.displayName }} 
       </span> 
      </template> 
     </test-child> 
     <button (click)="submitSelections()">Submit Selection</button> 
    ` 
}) 

export class ParentComponent implements OnInit { 
    data: any; 
    numbersData: any; 
    lettersData: any; 

    ngOnInit() { 
     this.getData(); 
    } 

    getData() { 
     // calling service and getting data for the game selections 
     this.testService.getData() 
      .subscribe(res => { 
       this.data = res; 
       setChildData(this.data); 
      }); 
    } 

    setChildData(data: any) { 
     for(let i = 0; i < data.sections.length; i++) { 
      if(data.sections[i].type === 'numbers') { 
       this.numbersData = data.sections[i]; 
      } 

      if(data.sections[i].type === 'letters') { 
       this.lettersData = data.sections[i]; 
      } 
     } 
    } 

    pickNumber(pickedNumbers: any[]) { 
     this.pickedNumbers = pickedNumbers; 
    } 

    pickLetter(pickedLetters: any[]) { 
     this.pickedLetters = pickedLetters; 
    } 

    submitSelections() { 
     // call service to submit selections 
    } 
} 

Детского компонент

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- 
      Content for child... 
      Showing list of items for selection, on click item is selected 
     --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    @Input() childData: any; 
    @Output() onSelection = new EventEmitter<Selection>; 

    pickedItems: any[]; 

    // used for click event 
    pickItem(item: any) { 
     this.pickedItems.push(item.number); 

     this.onSelection.emit(this.pickedItems); 
    } 
} 

Это более или менее код, который у меня есть. В основном у меня есть родитель, который обрабатывает выбор из дочерних компонентов, а затем я отправляю эти варианты в службу.Мне нужно передать определенные данные ребенку, потому что я пытаюсь получить объект, который возвращает ребенок в том формате, который ожидает служба. Это поможет мне не создавать весь объект, ожидаемый службой в родительском компоненте. Также это сделало бы ребенка повторно используемым в том смысле, что он отправит обратно то, что ожидает служба.

Update

Я понимаю, что пользователи по-прежнему размещения ответов на этот вопрос. Обратите внимание, что код, который был выше, работал для меня. Проблема была в том, что в конкретном шаблоне у меня была опечатка, которая привела к тому, что данные были undefined.

Надеется, что это оказывается полезными :)

ответ

7

вы можете использовать ngOnChanges, чтобы получить данные, как показано ниже,

ngOnChanges() { 
    if(!!childData){   
     console.log(childData);   
    } 
} 

OnChanges

Угловой вызывает метод ngOnChanges всякий раз, когда обнаруживает изменения в входных свойств компонента (или директивы).

Подробнее об этом here.

Update

ngOnChanges только огонь, если изменить весь объект, если вы хотите просто обновить свойство связанного объекта не весь объект у вас есть два варианта,

  1. либо привязать недвижимость прямо в этом шаблоне, обязательно используйте ?{{childData?.propertyname}}

  2. или вы можете создать заявку на покупку недвижимости в зависимости от объекта childdata,

    get prop2(){ 
        return this.childData.prop2; 
    } 
    

Copmlete Пример,

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

@Component({ 
    selector: 'my-app', 
    template: `<h1>Hello {{name}}</h1> 
    <child-component [childData]='childData' ></child-component> 
    ` 
}) 
export class AppComponent { 
    name = 'Angular'; 
    childData = {}; 
    constructor(){ 
    // set childdata after 2 second 
    setTimeout(() => { 
     this.childData = { 
     prop1 : 'value of prop1', 
     prop2 : 'value of prop2' 
     } 
    }, 2000) 
    } 
} 

@Component({ 
    selector: 'child-component', 
    template: ` 
    prop1 : {{childData?.prop1}} 
    <br /> 
    prop2 : {{prop2}} 
    ` 
}) 
export class ChildComponent { 
    @Input() childData: {prop1: string, prop2: string}; 

    get prop2(){ 
    return this.childData.prop2; 
    } 
} 

Вот Plunker!

Надеюсь, что это поможет!

+0

Спасибо за ваш ответ, кажется, указал мне в правильном направлении. Единственная проблема, с которой я столкнулся сейчас, состоит в том, что если данные были «простой» строкой, «ngOnChanges» обнаруживает изменения, однако, если это объект по какой-либо причине, он остается «неопределенным». –

+0

@ DanielGrima: Обновлен ответ. –

+0

Еще раз спасибо за вашу помощь ... Я только выяснил, в чем проблема. Имела опечатка в моей переменной шаблона, и поэтому она всегда была неопределенной, конечно. Не понял раньше! По крайней мере, похоже, что он работает, и, по-видимому, мне не нужны никакие «ngOnChanges» или геттеры для объекта. Теперь все в порядке. –

0

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

Имейте родительский огонь событие, возможно, dataSelected, и содержимое события будет выбранными данными.

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

Это поможет разделить ваши компоненты, что всегда является хорошей идеей.

0

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

Что вы можете сделать, зависит от вашего USECASE ..

Если вы просто хотите, чтобы отобразить данные в шаблоне ChildComponent , вы можете использовать оператор Элвиса (?). Что-то вроде: {{ childData?}} или {{ childData?.myKeyName }} ...

Если вы хотите сделать некоторые другие вещи, вы можете использовать сеттер в вашем ChildComponent. Она будет перехватывать изменения входного сигнала и даст вам возможность изменить/тест/«делать все, что вы хотите» с ним, прежде чем делать другие вещи с ним в вашем ChildComponent .. например .:

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    private _childData: any; 
    @Input() 
    set childData(parentData: any) { 
     // every time the data from the parent changes this will run 
     console.log(parnetData); 
     this._childData = parentData; // ...or do some other crazy stuff 
    } 
    get childData(): any { return this._childData; } 

    ngOnInit() { 
     // We don't need it for that... 
    } 
} 

или использование ngOnChanges как указано Madhu.

Вы также можете ознакомиться с записью поваренной книги для Component Communication в официальных документах.

+0

Да, это была опечатка, о которой вы говорили - я обновил вопрос еще несколькими пунктами. Я тоже пробовал ваше предложение, но я все равно получаю тот же результат:/ –

+0

не могли бы вы опубликовать исходный код? Я не на 100% понимаю, почему он не работает для вас ... Еще одна вещь, которую вы можете попробовать, - если вы не делаете ничего другого в своем ParentComponent с вашими данными из сервиса - поместите ваш 'testService.getData()' непосредственно в ваш дочерний вход и пусть угловой обрабатывает подписку с помощью 'async' pipe .. так:' ' –

+0

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

17

Поскольку данные не определено при запуске, вы можете отложить его * ngIf = «данные»

<div *ngIf='data'> 
    <test-child [childData]="data"></test-child> 
</div> 

Или вы можете осуществить ControlValueAccessor на компоненте и передавать его ngModel с ngModelChange

<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child> 
Смежные вопросы