質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.13%

【Angular】ComponentがServiceのデータを直接書き換えないようにしたい

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 741

score 17

前提・実現したいこと

Anguar8にて、ユーザー情報を複数のコンポーネントに提供するUserServiceと、UserServiceを利用するコンポーネントの一つ:ユーザー情報編集画面(UserEditComponent)を作成しています。
編集画面には入力欄と送信ボタンを配置し、送信ボタンをクリックするとUserServiceのユーザー情報を更新するようにしたいです。

発生している問題

編集画面の入力欄でユーザー情報を書き換えると、逐次UserServiceの内容も更新されてしまいます。
ServiceからComponentへのデータの渡し方を修正する?ことで、送信ボタンのクリックまでUserServiceの内容を書き換えない方法がないか探しています。

該当のソースコード

【user.ts】ユーザー名と年齢からなるUserクラスを定義しています。

export class User{
    name: string;
    age: number;
}


【user.service.ts】UserServiceでユーザー情報を保持し、複数のコンポーネントに提供する予定です。

import { Injectable } from '@angular/core';
import { User } from './user';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  user: User = { name: "Tanaka", age: 20 };
  getUser(): Observable<User>{
    return of(this.user);
  }

  updateUser(newUser: User){
    this.user = newUser;
  }

  //確認用
  logServiceUser(): void{
    console.log(this.user);
  }

  constructor() { }
}

【user-edit.component.ts】UserServiceから受け取ったユーザー情報をngModelを使ってInput要素にバインドし、編集画面を作成しました。
Input要素の内容を書き換えた後、Submitボタンがクリックされ(=onClick()が呼ばれ)たタイミングでUserService側の情報を更新したいのですが、実際にはInput要素を書き換えた時点でUserService側のデータも更新されてしまいます(checkServiceUser()関数を使って確認済み)。

import { Component, OnInit } from '@angular/core';
import { User } from '../user';
import { UserService } from '../user.service';

@Component({
  selector: 'app-user-edit',
  template: `
    <input type="text" [(ngModel)]="user.name">
    <input type="number" [(ngModel)]="user.age">
    <button (click)="onClick()">submit</button>
    <button (click)="checkServiceUser()">check</button>
  `
})
export class UserEditComponent implements OnInit {

  user: User;
  getUser(): void{
    this.userService.getUser()
    .subscribe( user => this.user = user );
  }

  onClick(){
    //Submitがクリックされた時点でuserServiceのデータを更新したい。
    this.userService.updateUser(this.user);
  }
  checkServiceUser(){
    console.log("check clicked");
    this.userService.logServiceUser();
  }

  constructor( private userService: UserService ) { }

  ngOnInit() {
    this.getUser();
  }

}

考えたこと

ユーザー情報がサービスからコンポーネントに渡される際、参照渡しで渡されるため、コンポーネント側でのデータ更新が直接サービスに反映されてしまっているのではと考えています。

このままでも「サービスを使った複数コンポーネント間のデータの共有」自体は非常に簡便に行えるのですが、送信ボタンのクリック時に内容確認の処理を挟む等、不都合ある場面が生じそうだと考えます。
また、上記のソースコードはサービスのプロパティをコンポーネントが直接書き換えているように思え、サービスが自身のメソッドでこれを行うように修正の必要があると考えています。

問題意識自体に誤りがある場合(内容確認は別途バリデーションの仕組みを使うべき・別にコンポーネントがサービスのデータを書き換えても問題ない、など)は恐縮ですが、ご指摘いただければ幸いです。

補足情報(FW/ツールのバージョンなど)

Angular: 8.2.14
Node: 12.14.0

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

こんにちは。
お気づきのようにオブジェクトが参照渡しであるがゆえの事象です。
オブジェクトを生成してメンバをコピーしてはどうでしょうか。

  user: User = new User();

  getUser(): void{

    u:User;

    this.userService.getUser()
    .subscribe( user => u.user = user );

    user.name = u.name.concat();
    user.age = u.age;

  }


※未検証なコードです。

オブジェクトのコピーには lodashユーティリティのcloneDeepメソッドを使用する方法が有ります。ユーティリティはnpmでインストールする必要がありますが。

javascriptでlodashを使ってディープコピーを簡単に行う

ご参考までに。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/05/31 16:16

    ありがとうございます! 必要に応じてlodashの導入も試してみます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る