Joshua Kemmerer
Joshua Kemmerer

Reputation: 1673

How do I get the value from a MdListOption element?

I'm having a lot of difficulty trying to use Angular Material, specifically with regards to the Selection List. I am displaying a list of objects in a Angular Material Selection List and I want to get the objects from the list of selected items.

This is what I have so far, but [value]="row" just gives me true when I call rows.selectedOptions.selected[0].value:

submit(rows) {
    console.log(rows);
    console.log(rows.selectedOptions.selected[0].value);
    console.log(rows.selectedOptions.selected[0]._getHostElement().innerText); // ugly hack, but at least it gives me the text of the option
}
<md-selection-list #rows dense>
  <md-list-option *ngFor="let row of config.data" [value]="row">
    {{row.title}}
  </md-list-option>
</md-selection-list>

<div class="d-flex justify-content-end">
  <div class="p-2">
    <button md-button (click)="submit(rows)">
      <span>Submit</span>
      <md-icon aria-label="submit">arrow_forward</md-icon>
    </button>
  </div>
</div>

How can I get the actual row from the list of selected options?

Upvotes: 3

Views: 3107

Answers (4)

Khurram Ali
Khurram Ali

Reputation: 1679

I've had the same problem in angular 5 .I ended up using SelectionModel and bind a click event

<mat-selection-list #AnswerableBaseline>
     <mat-list-option *ngFor="let a of dataSourceForAnswerable" (click)="onAnswerableBaselineListClicked(a)" checkboxPosition="before">
     </mat-list-option>
</mat-selection-list>

ts file

selectionBaseline   = new SelectionModel<Your Type>(true, []);
onAnswerableBaselineListClicked(row: any) {        
    if (!this.selectionBaseline.isSelected(row)) {
        this.selectionBaseline.select(row);
    }
    else {
        this.selectionBaseline.deselect(row);
    }

}

Upvotes: 0

user2968675
user2968675

Reputation: 834

I've had the same problem with not being able to extract value from MdListOption. I ended up using the workaround that kind of defeats the purpose, but still:

<md-selection-list #rows dense>
  <md-list-option *ngFor="let row of config.data" (click)="clickedOnRow(row)">
    {{row.title}}
  </md-list-option>
</md-selection-list>

and then keeping track of my selections in the clickedOnRow() in the component.

Upvotes: 0

esharp
esharp

Reputation: 1317

you could handle it with something like:

in your component.ts:

rows: Array<string> = []; //explicitly define and init property here

toggleRow(value:string){
    if(this.rows.indexOf(value) !== -1){
      this.rows.splice(this.rows.indexOf(value), 1);
    }else{
      this.rows.push(value);    
    }
}
submit(rows) {
  console.log(rows);
}

and your component.html:

<md-selection-list #rows dense>
  <md-list-option *ngFor="let row of config.data" (click)="toggleRow(row.title)">
    {{row.title}}
  </md-list-option>
</md-selection-list>

<div class="d-flex justify-content-end">
  <div class="p-2">
    <button md-button (click)="submit(rows)">
      <span>Submit</span>
      <md-icon aria-label="submit">arrow_forward</md-icon>
    </button>
  </div>
</div>

you could also use an Array<any> or similar and .push() the entire row object into your rows array (instead of just the .title)...

Upvotes: 2

Alex Beugnet
Alex Beugnet

Reputation: 4071

Look at the API documentation : https://material.angular.io/components/list/api

@Output() selectChange :
Emitted when the option is selected.

so I guess using

<md-selection-list #rows dense>
  <md-list-option *ngFor="let row of config.data" (selectChange)="onSelectOptionChange($event)">
    {{row.title}}
  </md-list-option>
</md-selection-list>

will give you the data you want in your TypeScript side :

onSelectOptionChange(value: any) {
  console.log(value);
}

Not tested, but that should work. (NO IT DOESN'T)

EDIT : I've been doing some testing in a plunkr, and here are my results :

Provided Plunkr for free laughters : https://plnkr.co/edit/1G3bgJ2Twue0RH7CGmK2?p=preview

I've been using the same code you provided :

<md-selection-list #rows dense>
  <md-list-option *ngFor="let row of config.data" 
  (selectChange)="onSelectChange($event)"
  (deselected)="onDeselected($event)"
  [checkboxPosition]="after"
  [value]="[testValue]" 
  [selected]="[testValue]">
    {{row.title}}
  </md-list-option>
</md-selection-list>

<div class="d-flex justify-content-end">
  <div class="p-2">
    <button md-button (click)="submit(rows)"> 
      <span>Submit</span>
      <md-icon aria-label="submit">arrow_forward</md-icon>
    </button>
  </div>
</div>

And here is the TypeScript I used for my tests

export class App {
  testValue: true;

  config = {
    data: [
      {
        title: 'Title1',
      },
      {
        title: 'Title2',
      },
      {
        title: 'Title3',
      },
      {
        title: 'Title4',
      }
    ]
  }

  constructor() {
  }

  onDeselected(value: any) {
    console.log('deselected event => ', value);
  }

  onSelectChange(value: any) {
    console.log('change => ', value);  
  }

  submit(rows) {
    console.log(rows.selectedOptions.selected.map(elements => {
      return elements._getHostElement().innerText;
    }));
  }
}

And the results :

  • Setting the value through an input is useless : it does set a value to the option-list-item which is not used for anything.
  • Setting the selected through an input is somewhat useless : It works when rendering, but here in the example I set them to true, and if I submit instantly, I get no result (rows.selectedOptions.selected is an empty array).
  • The selectChange Output event doesn't work.
  • The deselected Output event doesn't work.
  • I couldn't find any other way than you did to get the Option title simply. This is what I used to get it in an array (guess it's more practical) : console.log(rows.selectedOptions.selected.map(elements => { return elements._getHostElement().innerText; }));
  • @Input() checkboxPosition : Whether the label should appear before or after the checkbox. Defaults to 'after'. No, it defaults to 'before' !

How can I get the actual row from the list of selected options?

Conclusion : You can't and this component is utter garbage : Do not use it...

Upvotes: 2

Related Questions