Altering a public variable of a component from a sibling component

Within my application, I have two sibling components that are being set from the app.component:

<my-a></my-a>
<my-b></my-b>

The visibility of <my-a> is determined by a public variable in its component:

@Component({
  moduleId: module.id,
  selector: 'my-a',
  templateUrl: `<div *ngIf="visible">Hello World</div>`
})

export class MyAComponent {
    public visible = false;
}

I want to update the value of visible after clicking on an element within <my-b>:

@Component({
  moduleId: module.id,
  selector: 'my-b',
  templateUrl: `<div (click)="onClick()">Click Me</div>`
})

export class MyBComponent {
    onClick() {
        // logic goes here
    }
}

How can I change visible = true in <my-a> from <my-b>? Should this functionality be handled in the parent app.component?

UPDATE

After receiving some feedback, I was able to achieve the desired behavior using a few lines of jQuery:

@Component({
  moduleId: module.id,
  selector: 'my-b',
  templateUrl: `<div (click)="onClick('my-a')"></div>`
})

export class MyBComponent {
    onClick(element) {
        $(element).show(); // or hide() or toggle() or different actions
    }
}

Although this approach works well and can be easily scaled for multiple elements, I am concerned about whether using jQuery in Angular2 is considered good practice.

Answer №1

One way to achieve this is by utilizing the EventEmitter and Input in Angular.

Component X:

@Component({
  moduleId: module.id,
  selector: 'my-x',
  templateUrl: `<div *ngIf="isVisible">Hello World</div>`
})

export class MyXComponent {
    // listen for input variable passed by parent
    @Input() isVisible;
}

Component Y:

@Component({
  moduleId: module.id,
  selector: 'my-y',
  templateUrl: `<div (click)="onClicked()">Click Me</div>`
})

export class MyYComponent {
    // create EventEmitter to emit when onClicked method is called
    @Output() isVisible = new EventEmitter();
    onClicked() {
        this.isVisible.emit();
    }
}

And in the parent component:

@Component({
  moduleId: module.id,
  selector: 'parent',
  // pass isVisible variable to MyXComponent and listen for onClicked event from MyYComponent
  templateUrl: `<my-x [isVisible]="isVisible"></my-x>
                <my-y (onClicked)="toggleVisibility()"></my-y>`
})

export class ParentComponent{
    private isVisible: boolean = false;
    // toggle visibility based on emitted event from MyYComponent
    toggleVisibility() {
    if (this.isVisible === false) {
    this.isVisible = true;
    } else {
        this.isVisible = false;
        }      
    }
}

In more complex scenarios, consider using a shared service instead.

Answer №2

The concept is divided into three segments, all revolving around the main app.component. It can be broken down into specific tasks:

  1. The need to allow external sources to alter visibility. This can be achieved using an Input property
  2. Featuring a button whose purpose is to trigger an action on a different component. Therefore, it requires an Output property (in the form of an EventEmitter)
  3. The parent object can then connect to 's Output in order to set a variable that binds to 's Input.

Input property within MyAComponent

import { Component, Input } from '@angular/core';
@Component({
  moduleId: module.id,
  selector: 'my-a',
  template: `<div *ngIf="visible">Hello World</div>`
})
export class MyAComponent {
    @Input() visible: boolean = false;
}

This is how the parent component's HTML would look like to configure this:

<my-a [visible]="aVisibility"></my-a>

Output property in MyBComponent

import { Component, Output, EventEmitter } from '@angular/core';
@Component({
  moduleId: module.id,
  selector: 'my-b',
  template: `<div (click)="onClick()">Click Me</div>`
})
export class MyBComponent {
    @Output() makeVisible: EventEmitter<void> = new EventEmitter<void>();

    onClick() {
        this.makeVisible.emit();
    }
}

To listen to changes as a parent component, the HTML structure should resemble this:

<my-b (makeVisible)="makeAVisible()"></myb>

Integration through the parent component

import { component } from '@angular/core';
@Component({
    moduleId: module.id,
    selector: my-app,
    template: `
      <my-a [visible]="aVisibility"></my-a>
      <my-b (makeVisible)="makeAVisible()"></myb>
    `
})
export class MyApp {
    private aVisibility:boolean = false;

    makeAVisible() : void {
        this.aVisibility = true;
    }
}

Additional Information

This code has not been tested, so there may be typos and errors present. Additionally, if communication between components becomes complex, using a shared Service might be preferable. However, for a simple scenario like this, utilizing the parent component should suffice.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: The promise was not caught due to a network issue, resulting in a creation error

I'm trying to use Axios for API communication and I keep encountering this error. Despite researching online and attempting various solutions, I am still unable to resolve the problem. Can someone please assist me? All I want is to be able to click on ...

The embedded Twitter widget in the Angular 2+ app is visible only upon initial page load

After implementing the built-in function from Twitter docs into ngAfterViewInit function, my app works flawlessly. However, I encountered an issue where the widget disappears when switching routes. Here is the code that only functions on the initial page ...

When multiple Angular FormControls are present in a form, they always maintain their validity status

Currently, I have two inputs that are utilizing FormControl and always show as valid. However, when one is removed, the remaining input will display as either valid or invalid, as expected. Although I could replace the two form controls with a single Contr ...

Typescript is throwing a fit because it doesn't like the type being used

Here is a snippet of code that I am working with: import { GraphQLNonNull, GraphQLString, GraphQLList, GraphQLInt } from 'graphql'; import systemType from './type'; import { resolver } from 'graphql-sequelize'; let a = ({Sy ...

Display a nested component post initialization in Angular

<ng-container *ngIf="isTrue; else notTrue"> <app-child [property1]="value" [property2]="value" [property3]="value" (function1)="func($event)" ></app-child> </ng-container> <ng-t ...

Challenges with date formatting arise for Spanish speakers when the date returns as NaN or an Invalid

I have been working on an Angular app Objective: My aim is to allow users to input dates in Spanish format (DD/MM/YYYY) and display them as such, while converting them back to English format when saving the data to the Database. Issue: One problem I enco ...

customize your selections in p-multiselect with dynamic choices primeng angular 2

I am currently working on implementing the Primeng datatable. I have put together an array containing headers, fields, and options called headersList. Here is how it looks: { header: "Time", field: "time", options: "timeOptions" }, { header: ...

Achieving a delayed refetch in React-Query following a POST请求

Two requests, POST and GET, need to work together. The POST request creates data, and once that data is created, the GET request fetches it to display somewhere. The component imports these hooks: const { mutate: postTrigger } = usePostTrigger(); cons ...

How can the file system module (fs) be utilized in Angular 7?

Hello, I am currently working with Angular 7. I recently attempted to utilize the fs module in Typescript to open a directory. However, I encountered an error message stating: "Module not found: Error: Can't resolve fs". Despite having types@node and ...

What is the best way to link the data from the HTML input to the class property in the TypeScript file? (Combining Angular and IntroJs)

I'm working on an onboarding screen with Intro.js and need help with receiving user input. I've been trying different methods like [(ngModel)] = checked, [(checked)] = checked, (checked) = checked, but haven't had any success. Can anyone pro ...

Uncertain about the distinction between reducers and dispatchers when it comes to handling actions

I'm feeling a bit confused regarding reducers and dispatchers. While both receive actions as parameters, it doesn't necessarily mean that the actions I use in my dispatchers are the same as those used in my reducers, correct? For example, if I h ...

Issues with integrating Angular Cypress Component tests with Tailwindcss are causing problems

While implementing Tailwindcss in an Nx style monorepo structure with 'apps' and 'libs' folders, I configured the Tailwind file as follows: content: ['./apps/**/*.{html,ts}', './libs/**/*.{html,ts}'], However, despi ...

Angular Universal does not fully render on the server side

Currently, my project involves integrating Angular 4 with Angular Universal and Knockout. The main objective is to have the Angular application generate HTML on the server side for SEO purposes. As part of this process, I need to utilize KnockoutJs to bin ...

Step-by-step guide to designing a basic pagination/carousel using div elements in HTML and Angular 2

Hey there! I'm currently working on a project with an *ngFor in my template that generates multiple divs. <div *ngFor="let item of widgets" class="page-content widget-item"> <div>{{content}} </div> </div> I'd like to i ...

Leveraging Angular environment configurations for workspace libraries

I've been experimenting with Angular's latest version (>6) and am delving into the concept of using workspaces. One interesting feature is the ability to generate a library project with a simple CLI command: ng generate library api In my defau ...

What is the method for accessing a validator that has been declared in a reactive form group while within the scope of a custom

Currently, I have a custom component that I am happy with and it is being used in a reactive form as shown below. private init() { const control4 = new FormControl("yy", Validators.pattern(".{2}")); const control5 = new FormControl("zz", Validators.pa ...

Issue with ellipsis not functioning correctly on dynamic placeholders when they are in focus

When I focus on my dynamic placeholder input, the text-overflow: ellipsis property is lost for some reason. However, it is regained when blurred. Can anyone explain why this is happening? If you want to experiment with it, I have set up a stackblitz: htt ...

I am integrating and connecting my angular2 library with my primary project by placing it in the node_modules folder within the library's build directory. This process is expected to

I have developed an Angular2 component as a library and I am connecting this library to my main project. After building my library, a build folder is created. However, when I run npm-link inside the build folder, it generates a node_modules folder with al ...

Generic parameter with a union type

The proxy function returns a randomly determined type. const numbersArray = [1,2,3,4]; const stringsArray = ['1','2','3','4']; function func<T>(array: T[]): T[][] { return [[array[0], array[1]], [array[2], ...

Having trouble retrieving information from a JSON object? Unable to retrieve property 'company_about' of an undefined object?

Here is the JSON data I have: [ { "id": 1, "job_id": 1, "company_profile": "Sales and Marketing", "company_about": "Established in 1992 , it is a renouned marketing company", "company_product": "Ford,Mustang,Beetle", "key_skills": ...