mardi 2 avril 2019

How to write reusable component when sort of complexe dependency occurs

I'm working on angular since few weeks now, and I was wondering how can I write clean component without too much dependency and big reusability.

I have to say that my issue is related with big architecture where things already exists.

Lets say I need to write a component that display a choice with 2 Radio button : either you can select "Playlist 1" or "Playlist 2". This component have an Service called "PlayerService" that have a method called "Play" that take an array of Songs. The idea is to select a "Playlist 1" or "Playlist 2" and on a button click, call the PlayerService with the correct Playlist.

Here is a basic implementation : This code is for demonstration only.

BasicComponent.ts

import { PlayerService } from '../services/player.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-basic',
  templateUrl: './basic.component.html',
  styleUrls: ['./basic.component.css']
})
export class BasicComponent implements OnInit {

  private playlistArray = [
    { name: "playlist1", sounds: ["sound1.mp3", "sound2.mp3"]},
    { name: "playlist2", sounds: ["sound2.mp3", "sound3.mp3", "sound4.mp3"]}
  ]

  public SelectedPLayListName: string;

  constructor(private playerService: PlayerService) { }

  ngOnInit() {
  }

  onPlayButtonClicked(): void {
    var playList = this.playlistArray.find(x => x.name == this.SelectedPLayListName);

    this.playerService.Play(playList.sounds);
  }
}

PlayerSercice.ts

import { Injectable } from '@angular/core';

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

  constructor() {}

  public Play(sounds: any[]): void  {
    console.log("Start Play on a Playlist containing " + sounds.length + " elements");
  }
}

So as i mentioned above, we have the BasicComponent which have 2 playlist in an array, 2 radio button and a button to validate the choice. On validation the component gives the correct array to the PlayerService.

That's not the bahavior I would like / I can implement based on the current project achitecture.

The BasicComponent can't have the PlayList array in his own class. Instead I would like to being able to give him the playlist array from the parent component as an input for exemple. This can be done easly, declaring an @Input, etc etc.

That's still not the bahavior I would like / I can implement based on the current project achitecture.

I dont want my BasicComponent to have the PlaylistArray as an @Input, I only want one playlist object. The one that my user selected by clicking on my radio button INSIDE my BasicComponent. So I would like my BasicComponent being able to display the choice between Playlist1 and Playlist2, then somehow ask for the correct playlist from his parent and give this to the PlayerService

I came up with 2 way of doing this :

  • @Input / @Output :
    • Getting a playList from the parent component by an @Input parameter.
    • Then on user click on the PlayButton, the BasicComponent "emit his @Ouput" called "OnPlay" with the name of the selected playlist as argument
    • The parent react to this @Output and retrieve the good playlist and set it as input for the BasicComponent
    • The BasicComponent implement a logic that detect change on the Input and if it change because we emited the output, then he can call the play method on the service with this playlist.

I thinks there is a lot of ugly code behing this, mostly because of this latency between the @output.emit, and the actual change of the @input. This idea is not a really good one at all, but I thinked about it.

  • @Input a Service :
    • Creating an interface IPlayListProvider that containt a method GetPlayList(name: string).
    • Creating a service PlayListProvider and Inherit from IPlayListProvider.
    • Creating an @Input in my BasicComponent, of type IPlayListProvider.
    • So the parent can have (from DI in the constructor) and store an instance of the PlayListProvider.
    • So the Parent can pass this instance on the service as input to the BasicComponent.
    • The BasicComponent can now call the method GetPlayList with the SelectedPlayListName in the radio button choice when the user click on the PlayButton.

I like this implementation because this service isn't very coupled to my BasicComponent, I can reuse my component and provide an other service that also implement this interface and it will work. I can keep all the thing where they should be even if I will need to do some logic so, the PlayListProvider service can retrieve the playlist whereever they are. The Only things is, I dont know if its like Dangerous to do this, if it can break things in my Angular projects, if its against angular best practice.

Or maybe is there an other way ? (that keep things separated, and my component reusable)

What do you think ? How would you do this ?

Aucun commentaire:

Enregistrer un commentaire