Chris Rockwell
Chris Rockwell

Reputation: 1852

Update a list based on input field

I want to update a simple list based on input into a form field. Right now I have some dummy data setup:

protected comments: any = [
    {
        "id": "123",
        "date": "1511545812",
        "author": "rockwcl1",
        "message": "This is a message"
    },
    {
        "id": "1232",
        "date": "1511545812",
        "author": "rockwcl1",
        "message": "This is another message"
    }
]

getComments() {
    return this.comments;
}

addComment(comment: any) {
    this.comments.push(comment);
    console.log(this.comments);
}

My parent component:

import {Component} from '@angular/core';
import {IonicPage, NavController, NavParams} from 'ionic-angular';
import {Comment} from "../../comments/comment.service";

@IonicPage()
@Component({
    selector: 'page-event-discussion',
    templateUrl: 'event-discussion.html',
    providers: [
        Comment
    ]
})
export class EventDiscussionPage {
    event: any;
    comments: any;
    constructor(public navCtrl: NavController, public navParams: NavParams, private _commentApi: Comment) {
        this.event = this.navParams.data.event;
        this.comments = this._commentApi.getComments();
    }
}

event-discussion.html

<!-- removed some header stuff -->
<ion-content padding>
   <comment-thread [comments]="comments"></comment-thread> 
</ion-content>

<ion-footer>
  <ion-toolbar>
    <comment-form></comment-form>
  </ion-toolbar>
</ion-footer>

In my parent component, I pull in that dummy data and then pass it to a child:

<comment-thread [comments]="comments"></comment-thread>

comments-thread.ts

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

@Component({
    selector: 'comment-thread',
    templateUrl: 'comments-thread.html'
})
export class CommentsThreadComponent {
    @Input() comments: any[];
    constructor () {}
}

comments-thread.html

<ion-item *ngFor="let comment of comments">
  <comment [comment]="comment"></comment>
</ion-item>

comment.component.ts

import {Component, Input} from '@angular/core';
import { Comment } from '../comment.service'

@Component({
    selector: 'comment',
    template: '{{ comment.message }}'
})
export class CommentComponent {
    @Input() comment: any;
}

comment-form.ts

import {Component} from '@angular/core';
import {Comment} from '../comment.service';
import {Validators, FormBuilder, FormGroup } from '@angular/forms';

@Component({
    selector: 'comment-form',
    providers: [
        Comment
    ],
    template: `
      <form [formGroup]="_commentForm" (ngSubmit)="addComment()" (ngModel)="comments">
        <ion-item>
          <ion-input 
            type="text" 
            placeholder="Write a comment"
            formControlName="message"
            [(ngModel)]="newComment"></ion-input>
        </ion-item>
            <button ion-button type="submit" [disabled]="!_commentForm.valid">Submit</button>
      </form>
    `
})
export class CommentForm {
    private _commentForm: FormGroup;

    constructor ( private _formBuilder: FormBuilder, private _CommentApi: Comment) {
        this._commentForm = this._formBuilder.group({
            message: ['', Validators.required],
        })
    }

    addComment() {
        this._CommentApi.addComment({
            "id": "adsfa",
            "date": "1234455543",
            "author": "rockwcl1",
            "message": this._commentForm.value.message
        });
    }

}

I only recently started trying to figure out if [(ngModel)] is what I needed.

So, what's happening right now:

I guess I just expected magic - I updated the data so the list would update. What about this do I need to change in order to update the list when I new item is pushed?

[Edit: a link to a youtube video showing the console.log when a new comment is added: https://www.youtube.com/watch?v=NxHuDbcwvEE]

Upvotes: 0

Views: 87

Answers (2)

Richard Matsen
Richard Matsen

Reputation: 23533

I've simulated your code (minus the form, since you say the new messages are logging to the console), but it works ok here: Plunker.

So, change detection is working ok as-is.

For reference, here's the Plunker code. Could you please post your complete components in case the problem is within them.

@Component({
  selector: 'comment-thread',
  template: `
    <ul>
      <li *ngFor="let comment of comments">
        <comment [comment]="comment"></comment>
      </li>
    </ul>  
  `
})
export class CommentThreadComponent {
  @Input() comments;  
}

@Component({
  selector: 'comment',
  template: '{{ comment.message }}'
})
export class CommentComponent {
  @Input() comment;  
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <button (click)="addSomeComment()">Add a message</button>
      <comment-thread [comments]="comments"></comment-thread>
    </div>
  `,
})
export class App {
  name:string;
  comments: any;
  constructor(private _commentApi: Comments) {
    this.name = `Angular! v${VERSION.full}`;
    this.comments = this._commentApi.getComments();
  }
  addSomeComment() {
    this._commentApi.addComment({message: 'yet another comment'})
  }
}

ComponentAPI isn't a singleton

The problem is caused by the provider section in the form component.

@Component({
  selector: 'comment-form',
  // providers: [
  //     Comment
  // ],
  template: `

Essentially, since CommentAPI is holding the data you need it to be a singleton. But, you provide it in EventDiscussionPage and also in CommentForm, so Angular makes two instances.

The trick is to provide it once at a higher level than either of it's client components. Generally, app.module.ts is best.

Upvotes: 1

Aditya Dave
Aditya Dave

Reputation: 21

You need to use ChangeDetectorRef Module from @angular/core. You can have a look at the documentation for that here:

https://angular.io/api/core/ChangeDetectorRef

Upvotes: 0

Related Questions