Enhance the appearance of mat select dropdown in Angular by enabling nested values customization

I have been working on customizing angular material select/autocomplete to incorporate nested dropdowns for a project.

Specifically, I am looking to create a setup where there is one parent dropdown with multiple child elements. When a particular parent dropdown is expanded, only the corresponding child items should expand or collapse accordingly. Additionally, the checkbox event should be selected in this specific scenario.

However, I encountered an issue where selecting the dropdowns resulted in displaying [object object] instead of the desired outcome.

I have included console logs to showcase the selected and unselected values for troubleshooting purposes.

If anyone could offer assistance or insights on how to address this matter, it would be greatly appreciated.

STACKBLITZ

<mat-form-field appearance="fill">
    <mat-label>Toppings</mat-label>

    <input type="text" matInput placeholder="Select Users" aria-label="Select Users" matInput [matAutocomplete]="auto" [formControl]="states">
    <mat-autocomplete #auto="matAutocomplete">

      <mat-select-trigger>
        {{states.value ? states.value[0] : ''}}
        <span *ngIf="states.value?.length > 1" class="example-additional-selection">
              (+{{states.value.length - 1}} {{states.value?.length === 2 ? 'other' : 'others'}})
            </span>
      </mat-select-trigger>

      <mat-optgroup *ngFor="let group of stateList">
        <div>
          <mat-checkbox [checked]="group.selected" (change)="toggleParent($event, group)" (click)="$event.stopPropagation()">
            {{group.letter}}
          </mat-checkbox>
          <button mat-button (click)="expandDocumentTypes(group)">
                  <mat-icon>keyboard_arrow_down</mat-icon>
              </button>
        </div>
        <mat-option *ngFor="let name of group.names" [value]="name" [ngClass]="isExpandCategory[group.letter] ? 'list-show' : 'list-hide'">
          <mat-checkbox [checked]="group.checked" (change)="toggleSelection($event, name, group)" (click)="$event.stopPropagation()">
            {{name}}
          </mat-checkbox>
        </mat-option>
      </mat-optgroup>

    </mat-autocomplete>
  </mat-form-field>

  

   
  export class SelectCustomTriggerExample {
    constructor(private _formBuilder: FormBuilder) {}

    // stateForm: FormGroup = this._formBuilder.group({
    //   stateGroup: '',
    // });
    // toppings = new FormControl();
    isExpandCategory: boolean[] = [];
    toppingList: string[] = ['Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato'];
    stateRecord: any = [];
    states = new FormControl();

    expandDocumentTypes(group: any) {
      console.log("expanding dropdown", group);
      this.isExpandCategory[group.letter] = !this.isExpandCategory[group.letter];
      // expand only selected parent dropdown category with that childs
    }

    toggleSelection(event: any, name: any, group: any) {
      debugger;
      console.log("toggleSelection", name, event.checked, group);
      if (event.checked) {
        console.log("stastateRecordtelist", this.stateRecord);
        this.stateRecord.push(name);
        this.states.setValue(this.stateRecord);
        console.log("toggleselection ", this.states.value);
      } else {
        this.stateRecord = this.stateRecord.filter((x: any) => x !== name);
        console.log("else toggleselection", name, group, this.states.value);
        this.states.setValue(this.states.value.filter((x: any) => x !== name));
        console.log("after filter ", this.states.value);
        //this.states.setValue([]);
      }
    }

    toggleParent(event: any, group: any) {
      debugger;
      group.checked = event.checked;
      console.log("event", event.checked, "group", group, "states value", this.states.value);
      let states = this.states.value;
      states = states ? states : [];
      if (event.checked) {
        states.push(...group.names)
      } else {
        console.log("else", states);
        group.names.forEach((x: string) => {
          if (states.indexOf(x) > -1) {
            states.splice(states.indexOf(x), 1)
          }
        });
      }
      this.states.setValue(states);
      console.log("statesvalue", this.states.value);
      if (!event.checked) {
        this.states.setValue(this.states.value.filter((x: any) => !x.includes(group.names)))
        //this.states.setValue([]);
      }
      console.log("final statesvalue", this.states.value);
    }

    stateList = [{
        "letter": "A",
        "checked": false,
        "names": [{
            "id": 1,
            "type": "Alabama"
          },
          {
            "id": 2,
            "type": "Alaska"
          },
          {
            "id": 3,
            "type": "Arizona"
          },
          {
            "id": 4,
            "type": "Arkansas"
          }
        ]
      },
      {
        "letter": "C",
        "checked": false,
        "names": [{
            "id": 8,
            "type": "California"
          },
          {
            "id": 9,
            "type": "Colorado"
          },
          {
            "id": 10,
            "type": "Connecticut"
          }
        ]
      },
      {
        "letter": "D",
        "checked": false,
        "names": [{
            "id": 18,
            "type": "Delaware"
          },
          {
            "id": 19,
            "type": "Denwer"
          }
        ]
      }
    ];
  }
  

The desired output should resemble the image provided below:

https://i.sstatic.net/isWLe.jpg

Answer №1

Update your toggleParent method with the code provided below. For a working example, you can also refer to this stackblitz link: https://stackblitz.com/edit/angular-f5mizr-final-pxdgby

toggleParent(event: any, group: any) {
  debugger;
  group.checked = event.checked;
  console.log("event", event.checked, "group", group, "states value", this.states.value);
  let states = this.states.value;
  states = states ? states : [];
  if (event.checked) {
    states.push(...group.names.filter((x: any) => !states.includes(x.type)).map((x: any) => x.type))
  } else {
    console.log("else", states);
    group.names.forEach((x: any) => {
      if (states.indexOf(x.type) > -1) {
        states.splice(states.indexOf(x.type), 1)
      }
    });
  }
  this.states.setValue(states);
  console.log("statesvalue", this.states.value);
  if (!event.checked) {
    this.states.setValue(this.states.value.filter((x: any) => !x.includes(group.names)))
    //this.states.setValue([]);
  }
  console.log("final statesvalue", this.states.value);
  this.stateRecord = this.states.value;
}

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

Hide the button when you're not actively moving the mouse, and show it when you start moving it

How can I make my fixed positioned button only visible when the mouse is moved, and otherwise hidden? ...

Error in identifying child element using class name

I need help accessing the element with the class "hs-input" within this structure: <div class = "hbspt-form".......> <form ....class="hs-form stacked"> <div class = "hs_firstname field hs-form-field"...> <div class = ...

inline checkbox is positioned on top of the text rather than next to it

I am attempting to enhance my form by adding checkboxes, but I have encountered an issue with the bootstrap class "checkbox-inline" not working as expected. Below is the code snippet: <strong>Styles:</strong> <%= f.collection_check_boxes : ...

Avoiding the pitfalls of hierarchical dependency injection in Angular 6

Too long; didn't read: How can I ensure that Angular uses the standard implementation of HttpClient in lower level modules instead of injecting a custom one with interceptors? I have developed an Angular 6 library using Angular CLI. This library expo ...

Error encountered while trying to assign an extended NextPage type to a page component

I encountered a type error related to const Page, as shown in the screenshot and code block below. https://i.sstatic.net/FrAuz.png The error message states that Type '{ (props: Props): JSX.Element; getLayout(page: ReactElement<any, string | JSXEle ...

Using Typescript for testing React components: successfully passing an array of objects as props

My current approach involves passing an array of objects to mock component data for testing: const mockPackage = { id: '1232-1234-12321-12321', name: 'Mock Package', price: 8.32, description: 'Mock description', glo ...

Android devices still allow access to elements even when they are hidden using overflow

Contained within a parent div with overflow hidden is a Facebook-comments widget that is vertically cut off. This setup allows for showing only a portion of the content initially and expanding the parent container using jQuery. While this method works wel ...

Unable to trigger mouse event for a div that is positioned on top of an image

Similar Issue: Problem with IE: Transparent div over an image not triggering CSS:hover Greetings, I am currently experiencing a problem with the mouseover and mouseout events for a div that overlaps with an image. I am trying to display hotspots (high ...

Utilizing multiple dropdown buttons in navigation menu items with Bootstrap 4

Trying to find a solution for having sub menus within the menu items. There are 2 dropdown buttons (Reports and Views) within a menu item that is itself a dropdown item. Clicking on the first button displays the submenu below, but clicking on the second ...

support for wavesurfer.js in IE10 is not compatible with angular

In the process of developing an Angular application, I am utilizing the wavesurfer.js plugin to display media in wave format. While this functionality works smoothly in Chrome, it encounters issues in IE10 and IE11. Any assistance would be greatly apprecia ...

The image does not adjust size properly on mobile devices

How can I enable pinch-zoom and the ability to move around to view an enlarged image on mobile devices? https://i.sstatic.net/6JGRh.png Despite trying to add user-scalable=yes to the viewport meta tag, the image remains fixed within the viewport. Any sug ...

Use the resizable function for widths greater than 1024px

I have a resizable function on my page: $(function() { $( "#droppable" ).droppable({ create: function( event, ui ) {$( this ).hide(0)} }); $( "#droppable" ).on( "dropover", function( event, ui ) { $( this ) $( this ).text('¿Eliminar?&apo ...

The jQuery.addClass() function seems to be malfunctioning

I'm encountering an issue with the addClass() method. For some reason, it's not functioning properly in this scenario: https://jsfiddle.net/0g1Lvk2j/20/ If you scroll to the end and close the last box by clicking on the orange box, the orange b ...

"Enhance user experience with Angular Material: Popup Windows that preserve functionality in the original window while staying vibrant and accessible

Exploring Angular Material Dialog and other Popup Window Components for a project. Making progress but facing some challenges. Here are the requirements: a) The original screen should not be grayed out, b) Users should be able to interact with the windo ...

How to vertically center a form element in Bootstrap 4

As a newbie to using bootstrap 4 for the first time, I expected vertical alignment of elements to be easy, especially with flex being touted as the simplest way to achieve this. The issue: Struggling to vertically align my form element within my navigatio ...

The combination of mat-icon-button and mat-raised-button is not rendering properly in Angular Material 15

After upgrading to Angular v15 + Angular Material v15, the previous code that used Angular v14 + Angular Material v14 looked like this: https://i.sstatic.net/GjC30.png The code for the icon button is shown below: <button *ngIf="admin" ...

HTML Techniques: Flipping the Script - Writing Characters from Right to Left

In order to showcase the temperature in degrees Celsius, I have implemented a placement technique using absolute positioning. Consequently, the °C symbol will persist in its designated spot. To achieve the desired presentation, the text should be displaye ...

Set the boolean input property by checking for the existence of an attribute on the directive

Is it possible to set an input property for a directive by simply including it without passing a value? <my-component [customInput]='true'></my-component> However, with boolean input properties, I am having trouble figuring out how ...

What can be done to address the narrowing of the left div element as the right div's child elements grow in size?

Utilizing the power of flexbox, I have organized the UI into left and right panels. The child elements within the right panel are stacked vertically by utilizing the white-space: nowrap; CSS property. However, there seems to be an issue where increasing t ...

Incorporating a clickable button over a video

I've exhausted all available resources trying to figure this out and have hit a dead end. My goal is to place a button on top of the video, but my lack of CSS expertise is hindering me. Can someone please help me identify what I'm doing wrong wit ...