1

I have a class called friend, containing an array as a property:

export class Friend {
  id: number;
  name: string;
  bday: string;
  gifts: string[] = [];
}

I'm attempting to access this and push items into it, based on user input to a friend-detail component:

export class FriendDetailComponent implements OnInit {
  @Input() friend: Friend;


  constructor(
    private route: ActivatedRoute,
    private friendService: FriendService,
    private location: Location,
  ) { }

  ngOnInit(): void {
    this.getFriend();

  }

  getFriend(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.friendService.getFriend(id)
        .subscribe(friend => this.friend = friend);
  }

  goBack(): void {
    this.location.back();
  }

  addGift(): void {
    var giftinput = (<HTMLInputElement> document.getElementById("gift-input")).value;
    var gifts = this.friend.gifts;
    console.log(typeof(this.friend.gifts));
    console.log(this.friend.gifts);
    gifts.push(giftinput);
  }
}

The user input html looks like this:

<div *ngIf="friend">
  <h2>{{ friend.name | uppercase }} Details </h2>
  <div><span>id: </span> {{friend.id}}</div>
  <div>
    <label>Name:
      <input [(ngModel)]="friend.name" placeholder="name">
    </label>
    <br>
    <label>Birthday:
      <input [(ngModel)]="friend.bday" placeholder="bday">
    </label>
    <br>
    <label>Gift Ideas:
      <input id="gift-input" [(ngModel)]="friend.gifts" placeholder="gift">
      <p [(ngModel)]="friend.gifts" ngDefaultControl>{{friend.gifts}}</p>
      <button (click)="addGift()">add gift</button>
    </label>
    <!-- <button (click)="addGift()">Add Gift</button> -->
  </div>
</div>

<button (click)="goBack()">go back</button>

I don't believe this code is part of the problem, but just in case:

export class FriendService {
  //url to web api and friends object
  private friendsUrl = 'api/friends';


  getFriends(): Observable<Friend[]> {
    //this.messageService.add('FriendService: fetched friends');
    return this.http.get<Friend[]>(this.friendsUrl)
    .pipe(
      //_ refers to unused parameter
      tap(_ => this.log(`fetched friends`)),
      catchError(this.handleError('getFriends', []))
    );
  }

  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.error(error);
      this.log(`${operation} failed: ${error.messages}`);
      return of(result as T);
    }
  }

  getFriend(id: number): Observable<Friend> {
    //todo: send message after fetching the Hero
    //this.messageService.add(`FriendService: fetched friend id=${id}`);
    const url = `${this.friendsUrl}/${id}`;
    return this.http.get<Friend>(url).pipe(
      tap(_ => this.log(`fetched friend id=${id}`)),
      catchError(this.handleError<Friend>(`getFriend id=${id}`))
    );
  }

  updateFriend(friend: Friend): Observable<any> {
    return this.http.put(this.friendsUrl, friend, httpOptions).pipe(
      tap(_ => this.log(`updated friend id=${friend.id}`)),
      catchError(this.handleError<any>(`updateFriend`))
    );
  }

  addFriend(friend: Friend): Observable<Friend> {
    return this.http.post<Friend>(this.friendsUrl, friend, httpOptions).pipe(
      tap((newFriend: Friend) => this.log(`added friend w/ id=${newFriend.id}`)),
      catchError(this.handleError<Friend>(`addFriend`))
    );
  }

  searchFriends(term: string): Observable<Friend[]> {
    if (!term.trim()) {
      return of([]);
    }
    return this.http.get<Friend[]>(`${this.friendsUrl}/?name=${term}`).pipe(
      tap(_ => this.log(`found friends matching "${term}"`)),
      catchError(this.handleError<Friend[]>('searchFriends', []))
    );
  }

  deleteFriend(friend: Friend | number): Observable<Friend> {
    const id = typeof friend === 'number' ? friend : friend.id;
    const url = `${this.friendsUrl}/${id}`;

    return this.http.delete<Friend>(url, httpOptions).pipe(
      tap(_ => this.log(`deleted friend id=${id}`)),
      catchError(this.handleError<Friend>('deleteFriend'))
    );
  }

  private log(message: string) {
    this.messageService.add(`FriendService: ${message}`);
  }

  constructor(
    private http: HttpClient,
    private messageService: MessageService,) { }
}

When the addGift() function is called (button press) while the user input is empty, the print statements return 'object' and an empty array '[]'. However, as soon as I enter a value to the user input, the print statements show 'string' and the push function launches the error:

ERROR TypeError: gifts.push is not a function

What am I missing? Is the type changing, and why?

5
  • 1
    The value acquired from the input / ngModel is a string. How do you want a user's input to map to an array? One input for each gift would work, but I'm not sure if that's what you want. Commented Apr 19, 2019 at 19:39
  • What is the output of your console.logs prior to the call to gifts.push? Commented Apr 19, 2019 at 19:40
  • 1
    You shouldn't use [(ngModel)]="friend.gifts" on your input: it's not supposed to contain all the friend's gifts, but only the next gift that will be added to the array when the button is clicked. Bind it to a string property (nextGift for example) of your component. Commented Apr 19, 2019 at 19:41
  • Also, <p [(ngModel)]="friend.gifts" doesn't make much sense: you can't enter anything in a <p>. It's not a form control. Commented Apr 19, 2019 at 19:42
  • @LoganR.Kearsley When the user input is empty, it prints '[]'. When the user input is not empty, it returns the string input. Commented Apr 19, 2019 at 19:47

1 Answer 1

3

The type is indeed changing. You have stated in your class that .gifts is an array, but in your html you are using ngModel to bind it to a text input. When means that as your user is typing in the box, you are actively reassigning that value as a string.

this is the line where you might want to rethink / remove the ngModel

<input id="gift-input" [(ngModel)]="friend.gifts" placeholder="gift">
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.