Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor override the click event of an element in a package

I am trying to override the button click event for autoform-remove-item button as shown below, as I am trying to show a warning message ( before ) the user can remove any item in the Autoform array. Then if the user confirmed the item removal then the button click event shall continue normally. But I can't figure out how to override the click event of the button in a way to pause code below it (which I have no access to) until the user confirm / reject the deletion? Any help what I might be missing here? Thanks

    Template.salesInvoice.events({
       'click .autoform-remove-item': function(e){

            e.preventDefault();

            bootbox.dialog({
              message: "Are you sure you wish to delete this item?",
              title: "New Journal",
              buttons: {
                eraseRecord: {
                  label: "Yes!",
                  className: "btn-danger",
                  callback: function() {

                  }
                },
                doNotEraseRecord: {
                  label: "No!",
                  className: "btn-primary",
                  callback: function() {
                    //Continue with the normal button click event                

                  }
                }

              }
            });
       }

    });

The click event I am trying to override:

      'click .autoform-remove-item': function autoFormClickRemoveItem(event, template) {
        var self = this; // This type of button must be used within an afEachArrayItem block, so we know the context

        event.preventDefault();

        var name = self.arrayFieldName;
        var minCount = self.minCount; // optional, overrides schema
        var maxCount = self.maxCount; // optional, overrides schema
        var index = self.index;
        var data = template.data;
        var formId = data && data.id;
        var ss = AutoForm.getFormSchema(formId);

        // remove the item we clicked
        arrayTracker.removeFromFieldAtIndex(formId, name, index, ss, minCount, maxCount);
      },
like image 865
MChan Avatar asked Jun 27 '15 11:06

MChan


2 Answers

After running in circles for an hour or so, this is what I found out. Blaze installs it's event-listeners in-side the parent-element of the templateInstance. The property they are storred under is called $blaze_events. It's easy to use, but undocumented. Here's how you can use it:

Retrieving an event-handler

The handlers are stored inside an array ($blaze_events.handlers). So to find a specific one I wrote this function:

retrieveBlazeEvent = function retrieveBlazeEvent (parentElement, eventType, selector) {
  var returnHandler
  parentElement.$blaze_events[eventType].handlers.forEach(function (eventHandler) {
    if(eventHandler.selector === selector)
      return (returnHandler = eventHandler) && false
  })
  return returnHandler
}

Usage:

client/test.html

<template name="test">
  <p>Hi!</p>
</template>

client/main.html

{{ > test }}

client/main.js

Template.test.events({
  'click p': function () {
    console.log('PIII')
  }
})

retrieveBlazeEvent(document.body, 'click', 'p')

Remove an event listener

The event-handler returned by retrieveBlazeEvent has a function unbind. So here's an example

retrieveBlazeEvent(document.body, 'click', 'p').unbind()

The event-handler will still be there after you call this function. To revert it you can simply call bind on the same object.

Manually triggering an event-handler

Say we have a simple p-element. A click-event is being listened to:

Template.test.events({
  'click p': function () {
    console.log('PIII')
  }
})

To trigger the event-handler we'll need a p-element that matches this selector. We'll also need the template instance to retrieve the correct parent node. Then we need to call the event-handler with the view as its context. We also need to bind create a stub for the event, that contains currentTarget. This function does all that for you:

triggerEvent = function triggerEvent (eventType, elementSelector, context) {
  var context = context || document
  var element = context.querySelector(elementSelector)
  var eventStub = {
    currentTarget: element
  }
  var view = Blaze.getView(element)
  retrieveBlazeEvent(view._domrange.parentElement, eventType, elementSelector)
    .delegatedHandler
    .call(view, eventStub)
  return true
}
like image 107
halbgut Avatar answered Oct 09 '22 04:10

halbgut


Can't you just show the bootbox confirm in the code where the collection is removed? Then only remove it from the collection when the user confirms it. For example, I think this should help:

  'click .autoform-remove-item': function autoFormClickRemoveItem(event, template) {
        bootbox.dialog({
          message: "Are you sure you wish to delete this item?",
          title: "New Journal",
          buttons: {
            eraseRecord: {
              label: "Yes!",
              className: "btn-danger",
              callback: function() {
                  var self = this;
                  var name = self.arrayFieldName;
                  var minCount = self.minCount; // optional, overrides schema
                  var maxCount = self.maxCount; // optional, overrides schema
                  var index = self.index;
                  var data = template.data;
                  var formId = data && data.id;
                  var ss = AutoForm.getFormSchema(formId);
                  arrayTracker.removeFromFieldAtIndex(formId, name, index, ss, minCount, maxCount);
              }
            },
            doNotEraseRecord: {
              label: "No!",
              className: "btn-primary",
              callback: function() {
                //Continue with the normal button click event                

              }
            }

          }
        });
   }
  }
like image 22
NickTheTurtle Avatar answered Oct 09 '22 03:10

NickTheTurtle