1
votes

I have an observableArray bound using foreach binding to a containerDiv. As new items are added to the observable array, new divs are created inside the container.

Each dynamcically created div can be clicked (or tapped) which displays a kind of home-grown context-menu, with several options, two of which are "delete the div that you just clicked on" ahd "change the value of the div you just clicked on".

The contextmenu can figure out which div was clicked, and thereby can figure out which index in the observableArray must be deleted or have its value changed. But how does the context menu communicate this information back to the ViewModel?

Binding on the click event of the context-menu-option only tells the ViewModel which menu item was clicked; it does not reveal the other important piece of the information, the div tha twas responsible for opening the contextmenu, the one that must be edited or deleted.

How to feed the ViewModel a) the specific action that must occur (the menu choice) and b) the item in the observable array that must be acted upon?

3
Was this ever solved? I'm running into the same issue after refactoringRyan The Leach

3 Answers

0
votes

When you click to bring up the context menu in the first place, your handler will be called with the model element corresponding to the div. Save it and then use it when handling the click from the context menu (which will have to have come from that particular div).

0
votes

Not sure if I understand your problem, but maybe this can help you:

function MyVM(){
    var self = this;

    this.divsArray = ko.observableArray([]);

    this.handleDiv = function(div){
        //here you can raise your menu with the data provided from the div
        //in the delete callback of the menu, just pass to it the div object and call self.divsArray.remove(div);
    }
}


View:

<div data-bind = "foreach: divsArray">
     <div id = "foo-child" data-bind = "click: $parent.handleDiv($data)"></div>
<div>
0
votes
someGlobalNamespace.ContextMenuVM = new function () {
    var self = this;
    var piece = {};
    var args = [];

    self.show = function (data, event) {
        piece = this;
        args = arguments;
        var posx = event.clientX + window.pageXOffset; //Left Position of Mouse Pointer
        var posy = event.clientY + window.pageYOffset; //Top Position of Mouse Pointer
        $('#ContextMenu').popup('open', {
            x:posx,
            y:posy,
            positionTo:'origin'
        });
    };

    self.clickHandler = function(fn){
        return function(){
            $('#ContextMenu').popup('close');
            fn.apply(piece, args);
        };
    };
}();

Excusing my use of JQuery mobile for the show/hide this is how I solved it after reading other answers.

By caching 'this' which is bound to the vm of the element being initially clicked on / contextmenu'd and the arguments of the function, you can use Function.apply in order to delegate the click.

This may cause issues where the original click handlers could inspect the event, and see it differs from a click event, but that could be solved with some minor refactoring.

   <div id="ContextMenu" class="pieceContextMenu" data-role="popup" data-theme="c" data-dismissible="true">
        <div class="contextMenuItem CIicon Delete"    title="Delete"                data-bind="click: clickHandler(cms.canvasAreaVM.deleteUniformPiece)"><i style="cursor: pointer;" class="fa fa-times"></i><span class="contextMenuItemText">Delete</span></div>
        <div class="contextMenuItem CIicon Copy"      title="Clone"                 data-bind="click: clickHandler(cms.canvasAreaVM.copyFormPiece)"><i class="fa fa-copy"></i><span class="contextMenuItemText">Clone</span></div>
        <div class="contextMenuItem CIicon New"       title="Add new child"         data-bind="click: clickHandler(cms.addNewPieceVM.show)"><i class="fa fa-plus"></i><span class="contextMenuItemText">New</span></div>
        <div class="contextMenuItem CIicon NewParent" title="Add new parent"        data-bind="click: clickHandler(cms.canvasAreaVM.newParentFormPiece)"><i class="fa fa-plus-square"></i><span class="contextMenuItemText">New Parent</span></div>
        <div class="contextMenuItem CIicon CopyClip"  title="Copy to the clipboard" data-bind="click: clickHandler(cms.canvasAreaVM.copyFormPieceToClipboard)"><i class="fa fa-clipboard"></i><span class="contextMenuItemText">Copy</span></div>

    </div>

clickHandler is run immediately, and returns a function that will be called when clicked.