mardi 26 janvier 2016

Angular 2 Data Model

I'm trying to learn angular2, and I had a question about how to structure my data model. Suppose we have a leaderboard (here is the plunker). The leaderboard shows a list of players, their score, and a checkbox that indicates that you are or are not "following" that player. The players and their scores are populated from a backend that is periodically updated from the server, but the "followed" attribute is stored locally. The user will check that checkbox if they want to follow what that player is doing in the game that is currently ongoing. In the plunker, we have a LeaderboardComponent, which contains a series of LeaderboardItemComponents. The LeaderboardComponent has a LeaderboardService, which generates a new Leaderboard object every 2 seconds.

The plunker shows doing this the "wrong" way. The player name, score, and "followed" attribute are contained within the same model. Every 2 seconds, a new Leaderboard object is generated; if you check the checkbox, it will be checked until the next update, and then the check will disappear since an entirely new object is being made (and the server is not providing the "followed" attribute; that is specific to the user on the client side). What should happen is that the checkboxes will remain checked (and the model will maintain the correct "followed" state) even when a new Leaderboard object is generated.

The main solution I have considered is to completely separate the score and name from the "followed" attribute. They are currently all contained within the LeaderboardItem model. I could make a Followed model, as well as a FollowedService to persist the list of followed players...but I still need a "followed" attribute on the LeaderboardItemComponent, and it has to be manually kept in sync with the FollowedService when the new Leaderboard is generated. Is there more of an ng2 way to do this? Thank you so much for reading this far, and let me know if I can clarify. I have included the relevant code below (mostly excludes imports for length purposes):

AppComponent:

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2>Leaderboard</h2>
      <leaderboard>
      </leaderboard>
    </div>
  `,
  directives: [LeaderboardComponent]
})
export class App {
  constructor() {
    this.name = 'Angular2'
  }
}

LeaderboardComponent:

@Component({
    selector: 'leaderboard',
    providers: [LeaderboardService],
    bindings: [LeaderboardService],
    template: `
    <div *ngIf="loaded">
      <div>
        <leaderboard-item *ngFor="#li of leaderboard.items" [model]="li">
        </leaderboard-item>
      </div>
    </div>
  `,
    directives: [LeaderboardItemComponent]
})
export class LeaderboardComponent {
  @Input() leaderboard: Leaderboard;
  loaded: boolean = false;

  constructor(public leaderboardService:LeaderboardService) {
    leaderboardService.getLeaderboard()
      .subscribe(res => this.setLeaderboard(res));
  }

  setLeaderboard(leaderboard:any) {
      this.leaderboard = leaderboard;
      this.loaded = true;
  }
}

LeaderboardItemComponent:

@Component({
    selector: 'leaderboard-item',
    template: `
    <div>
      {{model.name}}
      {{model.score}}
      <input type="checkbox" [(ngModel)]="model.isFollowed">
    </div>
  `
})
export class LeaderboardItemComponent {
  @Input() model: LeaderboardItem;

  constructor() {
  }
}

LeaderboardService:

@Injectable()
export class LeaderboardService {
  leaderboard: Leaderboard;
  leaderboardStream;

  constructor(public http: Http) {
    this.leaderboardStream = Observable.timer(1, 2000)
        .map(data => this.generateRandomLeaderboard())
  }

  getLeaderboard() {
    return this.leaderboardStream;
  }

  generateRandomLeaderboard() {
    this.leaderboard = new Leaderboard();
    this.leaderboard.addItem(new LeaderboardItem("Player1", 1000*Math.random()))
    this.leaderboard.addItem(new LeaderboardItem("Player2", 1000*Math.random()))
    return this.leaderboard
  }
}

Leaderboard (model):

export class Leaderboard {
  name: string;
  items: LeaderboardItem[] = new Array();

  constructor() {
    this.name = "Top Players"
  }

  addItem(li:LeaderboardItem) {
    this.items.push(li)
  }
}

LeaderboardItem (model):

export class LeaderboardItem {
  name: string;
  score: string;
  isFollowed: boolean = false;

  constructor(name:string, score:string) {
    this.name = name
    this.score = score
  }
}




Aucun commentaire:

Enregistrer un commentaire