0

I'm trying to create a dumb UI component in Angular that's completely free from logic. Lets say I have a List component that gets an array of data as input. This is rendered as a plain list where each item can be clicked, causing an emit of a delete event (since no logic should be put inside the dumb component)

Now, the smart component that listens to this output triggers a confirm dialog. On YES, I want the dumb component to animate this removal from the list. On NO, do nothing. By updating the array for the input the data is taken care of, but what way could I inform the list to animate based of the condition from the smart component?

A simple solution would of course be to support a confirm dialog inside the dumb component but that would make it less dumb. Perhaps I don't want this confirm dialog in all scenarios, perhaps another dialog, or perhaps a db call to check if the user is allowed to etc...

@Component({
  selector: 'app-list',
  templateUrl: `
  <ol>
    <li *ngFor="let item of items; let i = index" (click)="delete(i)">
      {{ item }}
    </li>
  </ol>`,
  styleUrls: ['./list.component.css']
})
export class ListComponent<T> {

  @Input()
  public list: T[];

  @Output()
  public readonly deleteItem: EventEmitter<number> = new EventEmitter();

  public delete(index: number): void {
    this.deleteItem.emit(index);
  }
}
Max
  • 49
  • 1
  • 10

1 Answers1

1

I think you could make each list element into another type of component. Your parent dumb component will then instantiate and destroy each list element as an Angular component.

Then you can create a fade-in and/or fade-out animation inside your list element component.

Don't forget to use a correct trackBy function with a unique identifier for each list element, in order for your parent component to instantiate and destroy the correct element of your list !

https://www.kdechant.com/blog/angular-animations-fade-in-and-fade-out

You could also use a simple CSS animation on the class of your list elements !

https://stackoverflow.com/a/11681331/7635889

But the advantage of the first solution is that the animation will always be triggered when creating or destroying your child component.

[EDIT] Here is an approach I used and adapted to your code but it is not tested and it is a fade-in effect so you will have to do some tweaks.

@Component({
  selector: 'app-list',
  templateUrl: `
  <ol>
    <li *ngFor="let item of items; let i = index" (click)="delete(i)" [@simpleFadeAnimation]="'in'">
      {{ item }}
    </li>
  </ol>`,
  styleUrls: ['./list.component.css'],
  animations: [
    // the fade-in/fade-out animation.
    trigger('simpleFadeAnimation', [
      // the "in" style determines the "resting" state of the element when it is visible.
      state('in', style({opacity: 1})),
      // fade in when created. this could also be written as transition('void => *')
      transition(':enter', [
        style({ opacity: 0 }),
        style({ transform: 'translateY(10%)' }),
        animate('0.3s 0s ease')
      ]),
      // fade out when destroyed. this could also be written as transition('void => *')
     // fade out when destroyed. this could also be written as transition('void => *')
     transition(':leave',
     animate('0.3s 0s ease', style({opacity: 0, transform: 'translateY(10%)'})))
    ])
  ]
})
export class ListComponent<T> {

  @Input()
  public list: T[];

  @Output()
  public readonly deleteItem: EventEmitter<number> = new EventEmitter();

  public delete(index: number): void {
    this.deleteItem.emit(index);
  }
}
  • Thanks for your reply! This seems like a really good solution for the animation scenario. Although I was not fully aware how animations are handled separately on angular components (I'm quite new in the area) and I was hoping for a solution that would kind of communicate what should happen - rather than relying on angular creation/deletion. Lets expand on this example, what if I have a another button "Send to bottom" and only if the users selects yes, I want to programmatically scroll the list to the bottom. Somehow I want the dumb component to know the result from the smart component – Max Sep 12 '19 at 07:25
  • or does that make it a smart component...? – Max Sep 12 '19 at 07:36
  • 1
    Ok so if I get you right, you want your child dumb component to animate after some trigger in the parent smart component ? To do that you will need to have some kind of reference to your child component in the parent component. That is usually achieved with @ViewChild annotation. You will then be able to call a function in your child dumb component from the parent component, and that function will be responsible from animating the changes in the list ? Tell me if I understood you right, and if you need me to expand a bit more on my answer. – Zacharie Ménétrier Sep 12 '19 at 08:02
  • Oh yes, this seems to fulfill the need I have. Brilliant! – Max Sep 12 '19 at 08:27