简体   繁体   中英

Accessing view model from context menu callback with aurelia-slickgrid

I'm trying to replace aurelia-table with aurelia-slickgrid in a project I'm working on. I have a number of actions that are triggered when the user clicks an part of a table. I was thinking the context menu would be a way to replicate that behavior, but it seems in the callback for a menu command you don't have access to the view model. For example, I would like to be able to allow the user to change the status of a customer from the grid, but do that, I have to call the function that sends the update to the server.

My solution was to add a reference to the view model in the command item definition. My question is, is this the right way to handle this?

Thanks, Ross

You should probably use the Cell Menu instead of a Context Menu, they are built with the same structure (I know since I created those plugins), the only difference is that the Cell Menu (aka Action Menu) can be 1 (or more) column(s) in your grid while the Context Menu is only available via right+click from anywhere but is not visible at all in the grid (in short, if you want to show a column then use Cell Menu else Context Menu, they serve different purpose, a Cel Menu is typically for current row action while a Context Menu is for the entire grid).

When you say that the view model is not accessible in the Context Menu well that is not totally true, first off I'm not sure if you meant the item object and if that is the case then it is available in the 2nd argument args.dataContext but if you really mean the ViewModel Class of your Aurelia ViewModel, then just bind to it with .bind(this) or use it inline.

export class MyDemo {
  initializeGrid() {
    this.gridOptions = { 
      contextMenu: {
        command: myExternalContextMenu.bind(this), // bind this to external function
      }
    };
  }

  whatever() {}
}

External File

export function myContextMenu(e, args) {
  const item = args.dataContext;
  this.whatever(); // MyDemo class is available because of its bounded context
}

If we take for example the Cell Menu (Action Menu Column), we can write it this way

this.columnDefinitions = [
  { id: 'firstName', field: 'firstName', name: 'First Name' },
  { id: 'lastName', field: 'lastName', name: 'Last Name' },
  // ... more column defs
  {
    id: 'action', name: 'Action', field: 'action', width: 110, maxWidth: 200,
    excludeFromExport: true,    // you typically don't want this column exported
    formatter: actionFormatter, // your Custom Formatter
    cellMenu: {
      action: (e, args) => {
        console.log(args.dataContext, args.column); // action callback.. do something
      }
    }
  }
};

If you want to change a value in the grid from the Cell Menu action callback, you can do that via the SlickGrid DataView object which is available again indirectly from the args.grid , I mean indirectly because you need to get it from the Grid Object then after that you can get the DataView object from it.

action: (_event, args) => {
  const dataContext = args.dataContext;
  const grid = args.grid;
  const dataView = args.grid.getData(); // since we use DataView, getData() will return the DataView
  dataView.updateItem({ ...dataContext, status: true }); // update a status flag
  grid.invalidateRow(args.row); // invalidate that row will re-render that specific row
}

but there's actually another, and easier, way of changing a property of the item dataContext. The Cell Menu (and Context Menu) both have what is called an Option List which was created for that purpose of updating something in your item object (data context).

For example, if you want to change a flag you can do it this way

this.columnDefinitions = [
  { id: 'firstName', field: 'firstName', name: 'First Name' },
  { id: 'lastName', field: 'lastName', name: 'Last Name' },
  // ... more column defs
  {
    id: 'action', name: 'Action', field: 'action', width: 110, maxWidth: 200,
    excludeFromExport: true,    // you typically don't want this column exported
    formatter: actionFormatter, // your Custom Formatter
    cellMenu: {
      optionTitle: 'Change Effort Driven Flag', // optional, add title
      optionItems: [       
        { option: true, title: 'True', iconCssClass: 'fa fa-check-square-o' },
        { option: false, title: 'False', iconCssClass: 'fa fa-square-o' },
        { divider: true, command: '', positionOrder: 60 },
      ],
    }
  }
};

which produces the following menu, the Options are always on the top portion of the menu在此处输入图像描述

All of code shown in this answer comes from this Example 24 that shows both Cell Menu and Context Menus and their associated Cell Menu - Wiki and Context Menu - Wiki

Lastly, I'll finish by saying that Aurelia-Slickgrid (which I'm the author of) is a wrapper on top of SlickGrid. If you search enough, you will find that you can do most probably everything by searching with SlickGrid word and you'll find plenty. There's multiple ways to do what you want to do and in many situations just remember that the use of regular JavaScript will also help you ( .bind(this) is one of those)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM