Meteor override the click event of an element in a package
Asked Answered
B

3

5

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);
      },
Burseraceous answered 27/6, 2015 at 11:53 Comment(0)
C
2

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                

              }
            }

          }
        });
   }
  }
Contract answered 27/6, 2015 at 19:55 Comment(2)
This will require that I must install the package locally (under the application packages folder) then will be forced to manually update the package each time there is a new releaseBurseraceous
Obviously this is the best approach so far, thanks NickBurseraceous
P
6

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
}
Pharisaism answered 27/6, 2015 at 12:45 Comment(11)
Thanks, I have updated my question with the exact click event I am trying to override, as you can see it is not as simple as directly removing from a collection. Thanks againBurseraceous
The delete-button still in autoform? Because "NOTE: The afDeleteButton component that used to be part of autoform is now available as a separate package.".Pharisaism
This approach will require me to first know which collection this bottom is referencing which I don't know as this is not the app collection, instead it is an internal package collection where they save array items temporarily.Burseraceous
Sorry about that, I completely misunderstood the question. I edited my answerPharisaism
Thanks for your help but this approach won't help me in overriding the click event of the element in the package which is what I want to do as explained in my question. I could have helped this approach, but we are talking about an array of buttons, each have a unique package generated id...etc so i can never use this approachBurseraceous
I'm pretty sure that it will. You'll just have to add the div to all buttons, which isn't hard using JQ. I edited the second section again to make it clearer.Pharisaism
Assuming that this solution will solve it, that's not the solution I was seeking, as per my question title and description I am seeking a solution to override the package function / method. Thanks though for your help!Burseraceous
Took me three hours and some running in circles to find this solution (updated my answer). So please read it carefully before commenting. Hope it helps :) I learned a lot about blaze in the process, so it was worth it no matter what.Pharisaism
Thanks again, I tried implementing retrieveBlazeEvent and triggerEvent in my template but retrieveBlazeEvent always returns undefined. so do you mind showing me an example of how to implement it in any Meteor app template please? ThanksBurseraceous
Not a big fan of hacking around the internals of Blaze for something that should be that simple. Posted shorter and cleaner answer below. Also, IMHO deleting events of a 3rd party library is a really bad idea - cheersLiterally
@metakungfu I totally agree. My answer didn't actually help the user to solve his problem anyway. But it still has some valuable information about how blaze handles events IMO.Pharisaism
C
2

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                

              }
            }

          }
        });
   }
  }
Contract answered 27/6, 2015 at 19:55 Comment(2)
This will require that I must install the package locally (under the application packages folder) then will be forced to manually update the package each time there is a new releaseBurseraceous
Obviously this is the best approach so far, thanks NickBurseraceous
L
1

You can use stopPropagation to stop the event from bubbling up to autoform hooks.

So the short version:

Template.afArrayField.events({
  'click .autoform-remove-item': function(event) {
      if (!confirm('Are you sure you wish to delete this item?')) {
          event.stopPropagation()
      }
  }
})
Literally answered 15/12, 2016 at 2:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.