P.Juni
P.Juni

Reputation: 2485

How to modify other components by clicking one

I want to click any component of type CardComponent and then change variable currentState for others. But it seems like currentState cant be changed - it can only be changed for clicked element when using this.

let numInstances = 0;

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html',
  styleUrls: ['./card.component.scss'],
  animations: [
    trigger('change', [
      state('show', style({
        opacity: 1
      })),
      state('hide', style({
        opacity: 0
      })),
      transition('show=>hide', animate('150ms')),
      transition('hide=>show', animate('150ms'))
    ])
  ]
})
export class CardComponent {

  currentState = 'show';

  private instanceId: number;

  constructor() {
    this.instanceId = numInstances++;
  }

  get id() {
    return 'card_' + this.instanceId;
  }

  public toggle(): void {
    const items = document.getElementsByClassName('card-container'); // other items
    for (const item of items as any) {
      if (item.id !== this.id) {
        item.currentState = 'hide'; // current state is not changing
      } else {
        this.currentState = 'show'; // when using this, state is changing
      }
    }
  }
}

How can I change the currentState for other items ?

Upvotes: 0

Views: 37

Answers (1)

Yanis-git
Yanis-git

Reputation: 7875

on angular, most of the time, is bad idea to try to manipulate your DOM and access by vanillajs.

I recommand you to adapt your code by using the design recommanded by Angular Core team.

Container and UI component

that means you have components which represent your user interface. That components only use Input/Output to interact with rest of your application. That will help you to make your UI generic and easy to change.

They can looks like following :

@Component({
  selector: 'hello',
  template: `<h1 (click)="onClickHandler()">{{ isActive && 'Hello' || 'Bye' }} {{name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
  @Input() isActive: boolean = false;
  @Output() onClick: EventEmitter<string> = new EventEmitter();
  onClickHandler() {
    this.onClick.emit(this.name);
  }
}

Here in have basic UI component which can receive :

  • name (as id)
  • isActive state.

And emit :

  • when is clicked.

Then you have Container components, role of this component to make the bridge between your data storage, business logic, api request... And your UI.

It will looks like following :

export class AppComponent  {
  constructor(public storeService: StoreService) {}

  isActive(name: string): Observable<boolean> {
    return this.storeService.isActive(name);
  }

  setCurrent(name: string) {
    this.storeService.setCurrent(name);
  }
}

Then you have your store and business logic, most of the time inside Service

@Injectable()
export class StoreService {
  private currentActive$ = new BehaviorSubject('Bob');

  isActive(name: string): Observable<boolean> {
    return this.currentActive$.pipe(map((current) => current === name));
  }

  setCurrent(name: string) {
    this.currentActive$.next(name);
  }
}

Here you have one observable currentActive$ which will keep track of all change from your toggle.

You can update the current active by calling setCurrent method.

Now what your UI want is to know if him are the current active or not. For that you can use isActive(name: string): Observable<boolean>.

It simply take the global active stream, and map it to have stream of true or false

Online demo

Upvotes: 1

Related Questions