What is the method for incorporating sorting into a mat-list?

I've searched for various solutions, but none seem to work with mat-list. It's crucial for me because mat-list is the only solution where drag&drop functionality works (I always face this issue with mat-table in tables and I can't find a resolution). Below is a snippet of my HTML code that incorporates working mat-list with drag&drop, but I'm unsure how to add sorting:

<div *ngIf="viewList" fxFlex class="list-borders">
    <mat-list cdkDropList (cdkDropListDropped)="drop($event)">
      <mat-list-item>
        <mat-grid-list cols="16" rowHeight="50px" fxFlex  class="title-row">
          <mat-grid-tile colspan="4" class="title-tile">
            Name
          </mat-grid-tile> 
          <mat-grid-tile class="title-tile">
            Extension
          </mat-grid-tile> 
          <mat-grid-tile class="title-tile">
            Status
          </mat-grid-tile> 
          <mat-grid-tile class="title-tile">
            Size
          </mat-grid-tile> 
          <mat-grid-tile colspan="2" class="title-tile">
            Server version
          </mat-grid-tile> 
          <mat-grid-tile colspan="5" class="title-tile">
            Last modified (server)
          </mat-grid-tile> 
          <mat-grid-tile colspan="2">
            Segment name
          </mat-grid-tile>
        </mat-grid-list>
      </mat-list-item>
      <mat-list-item cdkDrag *ngFor="let element of Elements" (click)="navigate(element)" (contextmenu)="onContextMenu($event, element)" >
        <mat-grid-list cols="16" rowHeight="50px" fxFlex>
          <mat-grid-tile colspan="4">
            <mat-icon *ngIf="element.isFolder" color="primary">
              folder
            </mat-icon>
            <mat-icon *ngIf="!element.isFolder" color="primary">
              insert_drive_file
            </mat-icon>
            {{element.name}}
          </mat-grid-tile>
          <mat-grid-tile>
            {{element.extension}}
          </mat-grid-tile>
          <mat-grid-tile>
            <mat-icon *ngIf="element.status == 'online'" class="status-online">
              check_circle
            </mat-icon>
            <mat-icon *ngIf="element.status == 'unknown'" class="status-unknown">
              help
            </mat-icon>
            <mat-icon *ngIf="element.status == 'offline'" class="status-offline">
              report_problem
            </mat-icon>
          </mat-grid-tile>
          <mat-grid-tile>
            {{element.size}}
          </mat-grid-tile>
          <mat-grid-tile colspan="2">
            {{element.serverVersion}}
          </mat-grid-tile>
          <mat-grid-tile colspan="5">
            {{element.lastModified}}
          </mat-grid-tile>
          <mat-grid-tile colspan="2">
            {{element.segmentName}}
          </mat-grid-tile>
        </mat-grid-list>
      </mat-list-item>
    </mat-list>
  </div>

Attempt 1

This line of code:

<mat-grid-tile mat-sort-header="name" colspan="4" class="title-tile">

Results in:

More than one component matched on this element. Make sure that only one component's selector can match a given element. Conflicting components: MatGridTile,MatSortHeaderng(0)

Update 1

Elements:

export class Element {
  id?: string
  isFolder: boolean
  name: string
  parent: string
  extension?: string
  status?: string
  size?: number
  serverVersion?: string
  lastModified?: string
  segmentName?: string
}

And here are a few sample elements:

ngOnInit() {
    const folderA = this.fileService.add(
      { 
        name: 'Movies', 
        isFolder: true, 
        parent: 'root',
        status: 'online',
        size: 0,
        serverVersion: '5',
        lastModified: 'added yesterday',
        segmentName: 'IDK'
      }
    );
    // More fileService.add() calls for other elements
    this.updateFileElementQuery();

They were provided as input:

  @Input() elements: Element[];

Answer №1

I have made some adjustments to your code in order to accommodate the unconventional usage you need to apply (using a dataTable would be more appropriate for default sorting).

By doing this, I have set the groundwork for your future code restructuring.

html

<div *ngIf="true" fxFlex class="list-borders">

    <mat-grid-list cols="16" rowHeight="50px" class="title-row">
        <div class="flex">
            <mat-grid-tile *ngFor="let tile of tiles;" [colspan]="tile.cols" [rowspan]="tile.rows" [class]="tile.class"
                (click)="sortOnClick(tile);">
                <span class="flex"> {{tile.text}} <mat-icon>{{ !tile.direction ? 'arrow_drop_down' : 'arrow_drop_up' }}</mat-icon> </span>
            </mat-grid-tile>
        </div>
    </mat-grid-list>

    <div cdkDropList (cdkDropListDropped)="drop($event)" [cdkDropListData]="elements">
        <!-- (contextmenu)="onContextMenu($event, element)" -->
        <mat-list-item cdkDrag *ngFor="let element of elements">
            <mat-grid-list cols="16" rowHeight="50px" fxFlex>
                <mat-grid-tile colspan="4">
                    <mat-icon *ngIf="element.isFolder" color="primary">
                        folder
                    </mat-icon>
                    <mat-icon *ngIf="!element.isFolder" color="primary">
                        insert_drive_file
                    </mat-icon>
                    {{element.name}}
                </mat-grid-tile>
                <mat-grid-tile>
                    {{element.extension}}
                </mat-grid-tile>
                <mat-grid-tile>
                    <mat-icon *ngIf="element.status == 'online'" class="status-online">
                        check_circle
                    </mat-icon>
                    <mat-icon *ngIf="element.status == 'unknown'" class="status-unknown">
                        help
                    </mat-icon>
                    <mat-icon *ngIf="element.status == 'offline'" class="status-offline">
                        report_problem
                    </mat-icon>
                </mat-grid-tile>
                <mat-grid-tile>
                    {{element.size}}
                </mat-grid-tile>
                <mat-grid-tile colspan="2">
                    {{element.serverVersion}}
                </mat-grid-tile>
                <mat-grid-tile colspan="5">
                    {{element.lastModified}}
                </mat-grid-tile>
                <mat-grid-tile colspan="2">
                    {{element.segmentName}}
                </mat-grid-tile>
            </mat-grid-list>
        </mat-list-item>
    </div>
</div>

angular typescript list
shareeditflag

ts

import { Component, ViewChild, ElementRef, ChangeDetectionStrategy, Pipe, PipeTransform, ChangeDetectorRef } from '@angular/core';
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { orderBy } from 'lodash';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {

  elements = [
    {
      name: 'Movies',
      isFolder: true,
      parent: 'root',
      status: 'online',
      size: 0,
      serverVersion: '5',
      lastModified: 'added yesterday',
      segmentName: 'IDK'
    },
    {
      name: 'Trash',
      isFolder: true,
      parent: 'root',
      status: 'unknown',
      size: 0,
      serverVersion: '22',
      lastModified: 'added 2 years ago',
      segmentName: 'WTF'
    },
    {
      name: 'how_to_fix_it',
      isFolder: false,
      parent: 'root',
      extension: '.txt',
      status: 'offline',
      size: 2048,
      serverVersion: '1',
      lastModified: 'added 1 week ago',
      segmentName: 'NI'
    },
    {
      name: 'cute',
      isFolder: false,
      parent: 'root',
      extension: '.jpg',
      status: 'online',
      size: 4096,
      serverVersion: '12',
      lastModified: 'added today',
      segmentName: 'WUT'
    },
    {
      name: 'Game of thrones',
      isFolder: true,
      parent: 'temp',
      status: 'online',
      size: 0,
      serverVersion: '5',
      lastModified: 'added month ago',
      segmentName: 'OMG'
    }
  ];

  tiles = [
    { text: 'name', cols: 4, rows: 1, class: 'title-tile', direction: false },
    { text: 'extension', cols: 2, rows: 1, class: 'title-tile', direction: false },
    { text: 'status', cols: 2, rows: 1, class: 'title-tile', direction: false },
    { text: 'size', cols: 2, rows: 1, class: 'title-tile', direction: false },
    { text: 'serverVersion', cols: 2, rows: 1, class: 'title-tile', direction: false },
    { text: 'lastModified', cols: 5, rows: 1, class: 'title-tile', direction: false },
    { text: 'segmentName', cols: 2, rows: 1, class: 'title-tile', direction: false },
  ];

  drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex);
    }
  }

  constructor() {
  }

  sortOnClick(tile) {
    this.elements = orderBy(this.elements, [tile.text], [tile.direction ? 'asc' : 'desc']);
    tile.direction = !tile.direction
  }

}

css

.flex {
  display: flex;
}

You can access the StackBlitz demo here:

https://stackblitz.com/edit/add-angular-material-4aaevt

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

React Hook Form: Reset function triggers changes across all controllers on the page

I encountered an issue with using the reset function to clear my form. When I invoke the reset function, both of my form selects, which are wrapped by a Controller, get cleared even though I only defined default value for one of them. Is there a way to pr ...

Angular template containing a material table within a virtual scroll component

I am attempting to incorporate the content of ng-template into the cdk-virtual-scroll-viewport (using the library: ng-table-virtual-scroll), but I am encountering an error. <cdk-virtual-scroll-viewport tvsItemSize [footerEnabled]="true" ...

ERROR TypeError: Unable to access the 'nativeElement' property since it is undefined in Angular 5

I have encountered a problem while working on my application. Although similar issues have been asked before, I believe mine is slightly different. In my application, when a user deletes products from their cart, I want to automatically close the modal wi ...

Developing a user interface that filters out a specific key while allowing all other variable keys to be of the identical type

As I dive deeper into the TypeScript type system, I find myself grappling with an interface design query. Can anyone lend a hand? My goal is to craft an interface in TypeScript where certain object keys are of a generic type and all other keys should be o ...

Exploring the wonders of accessing POST request body in an Express server using TypeScript and Webpack

I am currently working on a Node and Express web server setup that utilizes Webpack, along with babel-loader and ts-loader. Let's take a look at some key portions of the code: webpack-config.js: const path = require("path"); const nodeExte ...

Enhancing nested structures in reducers

Currently, I am utilizing react, typescript, and redux to develop a basic application that helps me manage my ingredients. However, I am facing difficulties with my code implementation. Below is an excerpt of my code: In the file types.ts, I have declared ...

The Angular framework may have trouble detecting changes made from global window functions

While working, I came across a very peculiar behavior. Here is the link to a similar issue: stackblitz In the index.html file, I triggered a click event. function createClause(event) { Office.context.document.getSelectedDataAsync( Office.Coerci ...

Display no data when filters in ag-grid are empty

I'm currently utilizing the free version of ag-grid with AngularJS. Is there a way to display row data only when a filter contains content? I've gone through all the documentation, including gridoptions, but haven't been able to find a solu ...

How to retrieve the value of an input field in Angular 2/Typescript without using ngModel

Currently, I'm utilizing Typescript in conjunction with Angular2, mirroring the structure of the Angular2 Tour of Heroes guide. There is a specific input field that I aim to associate a change event with, triggering custom logic whenever the value wi ...

Converting JSON arrays into Python lists

I am encountering difficulties in parsing a JSON array I created into a Python list. Despite my efforts, I have not been successful in achieving this. Is there a way to convert my JSON array into a Python list? The structure of the json data I want to pa ...

Angular firebase Error: The parameter 'result' is missing a specified type and is implicitly assigned the 'any' type

I have encountered an issue with the code I am working on and both the result and error are throwing errors: ERROR in src/app/login/phone/phone.component.ts(48,75): error TS7006: Parameter 'result' implicitly has an 'any' type. s ...

Solving Angular Circular Dependencies

My popupservice allows me to easily open popup components: export class PopupService { alert() { this.matdialog.open(PopupAlertComponent); } yesno() { this.matdialog.open(PopupYesNoComponent); } custom() { this.matdialog.open(PopupCustomCompon ...

Error message: When attempting to redirect to another route, there is a type error with 'this' as it is implicitly assigned the type 'any'

I'm facing an issue with a component I've built using hooks. I want it to redirect to a different route when a function is called and the server response is received. However, my TypeScript is throwing an error: Type error: 'this' impl ...

Setting up a variable with no operation assigned to it

As I delved into the Angular utils source code, I stumbled upon this interesting line: export const NOOP: any = () => {}; It seems pretty straightforward - a variable that doesn't perform any operation. But then, within the same library, there is ...

Angular component linked to a dynamic object requiring user confirmation before changing or reverting to the original value

I've been working on getting a simple <select> behavior where the value reverts back if the user cancels the change. I managed to achieve it, but it took me quite a few hours and I'm not entirely satisfied with the implementation as it&apos ...

Customizing TinyMCE's font style menu options

Our platform utilizes TinyMCE as in-place editors to allow users to make live edits to content. However, a challenge arises when using a dark background with light text, as TinyMCE defaults to using this text color rather than black. (Please note: the the ...

Is there a way to alter the color of the weekday header in the Date picker calendar?

I'm working on enhancing the accessibility of my website by changing the default blue color of the weekdays header (highlighted area in the screenshot). I've attempted to modify the CSS code below, but no matter which color code I use, I can&apos ...

Angular library generation causing circular dependencies

Modification Required It has come to my attention that the issue of a circular dependency arises solely when utilizing a production build - ng build lib --prod My task involves creating an Angular library intended for utilization in a separate Angular pr ...

How to Utilize findIndex to Validate the Presence of Elements in an Array of Objects using TypeScript

I need assistance in checking which properties from an array are present in another array of objects and which ones are not. My object structure is as follows: var tempObj=[{id: '1', color: 'red, blue, green', age: 27},{id: '2& ...

Eliminate nested object properties using an attribute in JavaScript

I am working with a nested object structured like this const data = [ { id: '1', description: 'desc 1', data : [ { id: '5', description: 'desc', number :1 }, { id: '4', description: 'descip& ...