Using FlexGrid and ContextMenu with Knockout

Posted by: j.puetz on 8 August 2019, 11:38 pm EST

    • Post Options:
    • Link

    Posted 8 August 2019, 11:38 pm EST

    Hello again,

    I got the FlexGrid working with KnockoutJs.

    Now I am trying to integrate the wjContextMenu. ( https://www.grapecity.com/wijmo/api/classes/wijmo_knockout_input.wjcontextmenu.html )

    The example there works fine, but when I try to use that binding on the same class where I already have a wjFlexGrid, the Grid wont display data anymore.

    I get this error in JS console:

    Error: Unable to process binding "wjContextMenu: function(){return { id:'#contextmenu'} }"
    Message: You cannot apply bindings multiple times to the same element.
    

    This is my code:

    <div id="contextmenu" data-bind="wjMenu: { header: 'File', itemClicked: menuItemClicked}" style="display: none">
        <span data-bind="wjMenuItem: {}">New</span>
        <span data-bind="wjMenuItem: {}">open an existing file or folder</span>
        <span data-bind="wjMenuItem: {}">save the current file</span>
        <span data-bind="wjMenuSeparator: {}"></span>
        <span data-bind="wjMenuItem: {}">exit the application</span>
    </div>
    
    <p data-bind="wjContextMenu: { id: '#contextmenu'}">
        This paragraph has a context menu.
    </p>
    
    <div class="custom-flex-grid"
         data-bind="wjFlexGrid: {
            itemsSource: treeData,
            childItemsPath: 'childItems',
            selectionMode: 'ListBox',
            headersVisibility: 'Column',
         },
         wjContextMenu: { id: '#contextmenu'}">
        <div data-bind="wjFlexGridColumn: { binding: 'name', width: '*', align: 'left' }"></div>
        <div data-bind="wjFlexGridColumn: { binding: 'fileDate', width: '*', align: 'center'  }"></div>
        <div data-bind="wjFlexGridColumn: { binding: 'changedFromUser', width: '*' }"></div>
    </div>
    
    

    I hope you guys can help me with this.

    (Additional issue: The 3 colums have different widths, although they should be the same, but the 3rd one is too big.)

  • Posted 11 August 2019, 6:19 pm EST

    Hi,

    Please refer to the following sample which demonstrates how we could wjMenu as a context menu for flexgrid in the knockout and let us know if you face any issues:

    https://codepen.io/wijmo/pen/bXmJNL?editors=1010

    ~sharad

  • Posted 11 August 2019, 11:20 pm EST

    Hey Janis,

    Regarding the width of columns:

    We were able to replicate the issue at our end so we have forwarded the issue to the dev team with internal tracking id 393903. We will give you an update as soon as this issue is fixed.

    Regards,

    Ashwin

  • Posted 12 August 2019, 10:51 pm EST

    Hi Janis,

    The issue regarding star sizing of columns has been fixed in the latest nightly build. You can verify the same using the link below:

    https://stackblitz.com/edit/js-pgldrf

    PS: Nightly builds have gone through the QA cycle as all of our release builds do. Therefore, nightly builds are not suitable in a production environment.

    ~regards

  • Posted 2 September 2019, 1:33 am EST

    Hi Sharad,

    thanks for the sample. That does help indeed.

    I have a few follow-up questions though:

    1.) I want the context-menu to be specific to the clicked element (row). When I click on one of the options, I would expect to be able to get the hovered element somehow from the called function.

    Right now I solve this saving that value to a specific variable. Is there a better way to pass the current/hovered item to the menu?

    This is what I do:

    grid.hostElement.addEventListener("contextmenu", event => {
        var menu = model.menu;
        if (menu) {
            let hitTestInfo = this.treeGrid().hitTest(event);
            let dataItem = hitTestInfo.panel.rows[hitTestInfo.row].dataItem;
            this.menuItem(dataItem);
            menu.owner = grid.hostElement;
            event.preventDefault();
            menu.show(event);
        } else {
            console.log("menu not found");
        }
    }, true);
    

    I also tried calling it with an extra parameter, but that didnt work.

    menu.show(event, dataItem);
    

    2.) I also want to show different menu items depending on the data in the row you clicked.

    I found a demo, which sounds like it would do what I want:

    https://www.grapecity.com/wijmo/demos/Input/Menu/DynamicItems/purejs

    Do you have a knockout example of how to use this?

    My data gets lazy loaded into a hierarchical FlexGrid using knockout. Would this dynamic items approach fulfill my requirement to show different menu items depending on specific data in the tree?

    Best regards,

    Janis

  • Posted 2 September 2019, 5:58 pm EST

    Hi Janis,

    Regarding the current dataItem:

    After getting the current data item using the hitTest method, you may save it into a variable and in the itemClicked event handler, use this variable to get the data item.

    Regarding dynamic items:

    To change the menu items according to the data item, I would suggest you add all the menu items to the Menu and filter out the nonrequired menu items before calling the show method.

    Please refer to the sample attached that demonstrates both the behavior.

    Regards,

    Ashwin

    FlexGrid_ContextMenu.zip

  • Posted 19 September 2019, 5:56 pm EST

    Hi,

    Regarding the star sizing of columns:

    The issue has been fixed in the latest stable build (5.20192.631). You may verify the same using the sample below:

    https://stackblitz.com/edit/js-fydr9c

    ~regards

  • Posted 12 November 2019, 2:48 am EST

    Is there a different way?

    Because I tried the suggested solution, but the requirement is really to show dynamic entries in the menu. I don’t know them in advance and therefore cannot add them all beforehand.

    I also tried the knockout foreach, but I didnt get that working completely for the menu.

    
    <div id="contextmenu"
                    data-bind="wjMenu: { 
                        initialized: initMenu,
                        itemClicked: contextMenu
                    }"
                    style="display: none">
       <!-- ko foreach: { data: menuOptions, as: 'option' } -->
          <span data-bind="wjMenuItem: { value: option }"></span>
       <!-- /ko -->
    </div>
    
    

    This does dynamicly generate the menu items, but there is no way to controll the text shown. With the above example all entries will correctly trigger when clicked, but none of them has a text to click on. You can set a default, that will be shown for all the items insted of empty, but thats not helpful, since its the same for all entries and not dynamic.

    Alternatives I tried:

    I tried using a knockout text-binding as well.

    <span data-bind="wjMenuItem: { value: option }, text: option">
    

    This will cause a knockout error though, because 2 binding try to control the content.

    I tried generating the text inside like this:

    <span data-bind="wjMenuItem: { value: option }">
                            <!-- ko text: option -->
                            <!-- /ko -->
                        </span>
    

    In them HTML-Dom it looks like it works, but in the menu the entries still don’t have any text.

    I also tried setting the text-binding within wjMenuItem.

    <span data-bind="wjMenuItem: { value: option, text: option}">
    

    Well this isnt documented and doesnt work. I was just beeing optimistic. This would be the best solution imo, so maybe a possible improvement?

  • Posted 12 November 2019, 9:25 pm EST

    Hi Janis,

    For displaying dynamic menu items, you could update the itemsSource of the menu before calling the show method. Please refer to the following code snippet and the sample demonstrating the same:

    grid.hostElement.addEventListener("contextmenu", function(e){
          var menu = model.menu;
          if(menu){
            menu.owner = grid.hostElement;
            e.preventDefault();
            var ht = grid.hitTest(e);
            var prefix = "";
            switch(ht.panel.cellType){
              case wijmo.grid.CellType.Cell: prefix = "cell "; break;
              case wijmo.grid.CellType.ColumnHeader: prefix = "col-header "; break;
              case wijmo.grid.CellType.RowHeader: prefix = "row-header "; break;
            }
            var newSource = [];
            if(prefix){
              newSource = getMenuData(prefix + ht.row + ":" + ht.col);
            }else {
              newSource = getMenuData("default ");
            }
            menu.itemsSource = newSource;
            menu.show(e);
          }else{
            console.log("menu not found");
          }
        }, true);
    

    https://codepen.io/wijmo/pen/VwwGVRZ?editors=1010

    Regards

  • Posted 5 August 2020, 8:03 am EST

    This had helped me a lot!

    Now I ran into another issue, when I wanted to add submenu entries.

    I created a new thread for that here:

    https://www.grapecity.com/forums/wijmo/creating-submenu-using-kno

    Help with that would be much appreciated.

    Regards

Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels