Creating a package, making events that apply globally

Let’s say I wanted to make my own form validator, something really simple:

  'submit form': (e) ->
    e.preventDefault()
    $form = $(e.target)

    $form.find('[data-required]').each ->
      if $(@).val() is ''
        $(@).closest('.form-group').addClass 'has-error'

  'focus .has-error input': (e) ->
    $(e.target).closest('.form-group').removeClass 'has-error'

I’d want these events to fire on every single form in every template. How do I make it do that? I’m guessing UI.body.events might work, but unfortunately it’s broken. I guess the plus side of a package is that I can add gwendall:body-events to the dependency list.

Ok, I tried that by editing package.js:

...
Package.onUse(function(api) {
  api.versionsFrom('1.0.4.2');
  api.addFiles('form-validator.coffee');
  api.use(['jquery', 'gwendall:body-events']);
});
...

I tried encasing the above events in Template.body.events() and it didn’t work. I’m out of ideas!

Oh, I suppose I should add that if the form validation fails, any other submit form events will not get called.

Take a look at this:

https://atmospherejs.com/aldeed/template-extension

Thanks for the link. I’m surprised there’s no built-in way to do this. Maybe I’m going about this the wrong way and should find some way to include a generic form template that can wrap any template a user wants. So for example, in my validator package, the instructions might say to make a form by doing this:

{{> validatedForm template="usersLoginForm"}}

Then my package contains this template:

<template name="validatedForm">
  {{includeUserForm template}}
</template>

and this helper:

Template.validatedForm.helpers
  includeUserForm: (templateName) ->
    Blaze.render templateName, ...???

I’m not sure how to carry that out exactly, but the idea is that validatedForm would have the events for validating fields on it, and that template would contain a user-specified template within it so that the events would apply to whatever that user-specified template contains.

I’ll have to experiment a bit with this.

Instead of creating a template, you can create a function that takes in a template instance as its parameter. Your package then can be called at the developer’s discretion either from an event map or anywhere else they want.

Basicallay, your package woud be

MyPackage = function(templateInstance, options, callback) { 
  /* do something or return the form object, or errors etc */ 
}

and if I want to use your package, I could do something like:

Template.myFormTemplate.events({
  '#myForm submit' : function(e,t) { 
    MyPackage(t, function(e,r) { /* do something */ }); 
  } 
}) 

So basically, I will have taken my template instance and offloaded its validation handling to your package’s exported function which returns a callback I can consume with its error or result object.

And the package will have:

  • had access to the exact template that the dev is concerned with
  • been decoupled from ui/template code

PS: I have not given too much thought to this but kind of feels right this way

Hmm… I like that. Makes sense. Actually it’s better because it provides more control, rather than just snooping on all forms and not giving the developer a say on which forms get validation or not.

So it could work like this:

Template.myFormTemplate.events
  'submit #myForm': (e, t) ->
    validates = MyPackage t, ...

    if validates
      ...proceed...
    else
      ...alert user...

Thanks, man! Sometimes it helps bouncing ideas off other people, especially when I’ve been buried in code and docs for the past several days with no break. :wink:

I’m glad it works out for you :slight_smile:

Also, I suggest keeping a callback option so that you don’t lose reactivity. It would be great to decide myself if I want the validation to be sync or async.

1 Like

Ah, good point, and makes sense!