In Meteor, how to create a common event handler?

I want to create a common event handler that I can reuse on dom elements, so I donโ€™t need to write the boiler panel again and again. I thought I understood, but I get errors.

The problem I am facing is that I believe that the event handlers are connected at a different time than I need. Maybe the document. Where I think I need to attach them using the .live () method? Although I, perhaps, have no idea what I'm talking about here.

Here is what I am trying to do:

Multipage application.

Several collections into which data should be inserted.

Button code to display the insertion form.

<button id="btnShowInsert" class="btn btn-success" rel="tooltip" title="add group"> <i id="btnIcon" class="icon-plus-sign icon-white"></i> </button> 

A template that displays a form based on a page (controller)

 {{> groups_insert}} 

Here is the form.

 <template name="groups_insert"> {{#if acl_check}} {{> alert}} < p> < form class="form-horizontal well hide" id="insert"> <fieldset> < div class="control-group"> < label class="control-label" for="name">Name</label> < div class="controls"> < input type="text" class="input-xlarge" id="name" name="name"> < /div> < /div> < div class="form-actions well"> < button id="btnReset" type="reset" class="btn btn-large">Reset</button> < button id="btnSubmit" type="button" class="btn btn-primary btn-large">Submit</button> < /div> < /fieldset> < /form> < /p> {{/if}} < /template> 

Here is the client code for implementing the button that displays the form on the page.

 Template.groups.events[ Meteor.eventhandler.btn_events('#btnShowInsert') ] = Meteor.eventhandler.make_btn_show_insert_form_click_handler(); 

Here is my general event handler

 var EventHandler = Base.extend({ btn_events: function(selector) { return 'click ' + selector; //, keydown '+selector+', focusout '+selector; }, make_btn_show_insert_form_click_handler: function(){ //var click = options.click || function () {}; return function (event) { if (event.type === "click") { event.stopPropagation(); event.preventDefault; try{ if ($('#btnIcon').hasClass('icon-plus-sign') ) { $('#btnIcon').removeClass('icon-plus-sign'); $('#btnIcon').addClass('icon-minus-sign'); } else { $('#btnIcon').removeClass('icon-minus-sign'); $('#btnIcon').addClass('icon-plus-sign'); } $('#insert').slideToggle('slow', 'swing'); } catch(error) { Alert.setAlert('Error', 'Critical Error: ' + error, 'alert-error'); } } } }, }); Meteor.eventhandler = new EventHandler; 

ERROR

 Uncaught TypeError: Cannot call method 'btn_events' of undefined 

BUT , if I define an event handler this way and call it that way, it works.

 Template.groups.events[ btn_events('#btnShowInsert') ] = make_btn_show_insert_form_click_handler(); var btn_events = function (selector) { return 'click ' + selector; //, keydown '+selector+', focusout '+selector; }; var make_btn_show_insert_form_click_handler = function () { //var click = options.click || function () {}; console.log( Meteor.request.controller ); return function (event) { if (event.type === "click") { event.stopPropagation(); event.preventDefault; try{ if ($('#btnIcon').hasClass('icon-plus-sign') ) { $('#btnIcon').removeClass('icon-plus-sign'); $('#btnIcon').addClass('icon-minus-sign'); } else { $('#btnIcon').removeClass('icon-minus-sign'); $('#btnIcon').addClass('icon-plus-sign'); } $('#insert').slideToggle('slow', 'swing'); } catch(error) { Alert.setAlert('Error', 'Critical Error: ' + error, 'alert-error'); } } } }; 

Problem I do not want to replicate the code throughout the site in order to implement a beautiful button that can slide and form on any page. If I could get its abstraction, then I could have a button Show form type on all pages for any collection that I am rendering that allows you to enter data. In addition, this leads to the fact that you can create a single-form handler for all forms, and then bind them to the controller through the action of the model.

Any ideas?

+6
source share
4 answers

You can bind a high-level template to elements created using child templates. Then you only need to bind once. for instance

HTML:

 <template name="settings"> {{> login_settings }} {{> account_settings }} {{> data_settings }} </template> <template name="login_settings"> <btn class="slideToggle">Slide me for login!</btn> </template> <template name="account_settings"> <btn class="slideToggle">Slide me for account!</btn> </template> <template name="data_settings"> <btn class="slideToggle">Slide me for data!</btn> </template> 

JavaScript:

 Template.settings.events { 'click .slideToggle': function() { var clickedElement = event.target; // add/remove CSS classes to clicked element } }; 

So, if you create 10 different template definitions in the settings, so you only need to bind the handler to only one template.

+10
source

I feel that you are too offended. Why not do it?

 Template.someTemplate.events({ 'click .button': buttonClicked }); function buttonClicked(evt) { // DRY code to handle a button being clicked } 

This has the right balance of separation: your event handler is defined once, but you can tell each template that you want its buttons to listen to an event. And if it's not good enough, you can distract him even more:

 Template.someTemplate.events(genericEvents); 

And it's possible to even combine genericEvents with specific events for this template, if you want.

+3
source

Here is what I ended up doing. The example shows only the general insert handler.

 var EventHandler = Base.extend({ btnClickHandler: function(){ return function (event) { event.preventDefault(); Meteor.eventhandler[event.currentTarget.id](event); } }, insert: function(event){ event.preventDefault(); var params = $('#insert-form').toJSON(); try{ window[Meteor.request.controller.capitalise()]['validateParams'](params); var ts = new Date(); params.client_updated = ts; var has_popup = params.has_popup; delete params.has_popup; window[Meteor.request.controller.capitalise()]['insert'](params, function(error, _id){ if(error){ Alert.setAlert('Error', error, 'alert-error', true, has_popup); } else { Alert.setAlert('Success', 'Record successfully created.', 'alert-success', true, has_popup); $("#insert-form").reset(); Meteor.flush(); } }); } catch(error) { Alert.setAlert('Error', error, 'alert-error', true, params.has_popup); } } }); Meteor.eventhandler = new EventHandler; 

Now I just need to create descriptor templates without any significant javascript code to handle the generic events and link them as follows.

 $(document).on("click", '#print', Meteor.eventhandler.btnClickHandler()); $(document).on("click", '#insert', Meteor.eventhandler.btnClickHandler()); $(document).on("click", '#remove', Meteor.eventhandler.btnClickHandler()); $(document).on("click", '#removeSubField', Meteor.eventhandler.btnClickHandler()); $(document).on("click", '#insertSubField', Meteor.eventhandler.btnClickHandler()) $(document).on("click", '#update', Meteor.eventhandler.btnClickHandler()); $(document).on("click", '#updateSubField', Meteor.eventhandler.btnClickHandler()); $(document).on("click", "#toggleActive", Meteor.eventhandler.btnClickHandler()); $(document).on("click", "#toggleChild", Meteor.eventhandler.btnClickHandler()); 

Now I do not need to write any template event cards for processing basic CRUD. I can create any number of handle patterns if / route matches the collection name. Although from time to time I do some complex conversions. Basically, an event handler generates events based on the aka request.controller route into the collection and abstracts it through a common client / server data model to verify and even control access along with what exists in Meteor.

It seems to work well and significantly reduced my code base. I have dozens of collections in which I did not have to write event map handlers, because the main CRUD is processed, but abstracted enough so that I can configure checks, security, and other health checks in the client / server shared data model.

+1
source

The approach I took to this problem in Meteor 1.0.2 is the use of dynamic templates. See Dan Dascalescu's canonical answer and docs .

Say you have a set of common events related to pattern "A" and you want to use them in patterns "B", "C" and "D."

HTML:

 <template name="A"> {{> Template.dynamic template=myTemplate}} </template> 

JS:

 Template.A.events({ ... your event code }) 

You define a helper function for "A" that dynamically selects which of B, C or D (...) you want to include:

 Template.A.helpers({ // dynamically insert a template myTemplate: function(){ if (...) return 'B'; // return a string with the name of the template to embed if (...) return 'C'; if (...) return 'D'; } }) 

Events defined in "A" will now be available in "B", "C" and "D."

Please note that pattern "A" must not contain any HTML code.

0
source

Source: https://habr.com/ru/post/921929/


All Articles