Sharing common javascript code / functions between components


#1

Hi there,

I can’t figure out how I can share some simple JavaScript functions between components.

For example, see below some code for an action inside a component. I want to share the function deactivate_other_filter_menus inside an action in another component but after a lot of searching on the net, I don’t see a clear solution.

TIA

-Emmanuel

import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    filtrer(filtre, event) {
      let elem = Ember.$(event.target);
(...)
      function deactivate_other_filter_menus(my_buttons_names) {
        Ember.$(my_buttons_names).each(function(index) {
          Ember.$("button[data-filtre='" + my_buttons_names[index] + "']")
          .removeClass('active')
        });
      }
 (...)
      return true;
    }
  }
});

#2

Can you just use a util or a mixin? Mixin would put that function on the component so you’d call it with this.deactivate_other_filter_menus..., util would import it without mixing it in, so you could just reference it as if you’d declared it at the top of your component file.


#3

Thanks for the answer dknutsen,

In the end I went with a util. I couldn’t find good info though on the ember website beside this link:

https://guides.emberjs.com/v2.14.0/tutorial/service/#toc-toggle

I also realized that utilities were in fact ES6 modules. This link helped me a lot:

javascript - ES6 export default with multiple functions referring to each other - Stack Overflow

For the sake of helping others facing the same problem, here what I did because I needed more than one functions, and needed the ability for these functions to refers to the functions in the ES6 module/util:

// app/utils/filtres-communs.js
function deactivate_other_filter_menus(my_buttons_names) {
  Ember.$(my_buttons_names).each(function(index) {
    Ember.$("button[data-filtre='" + my_buttons_names[index] + "']")
    .removeClass('active')
  });
}

function hide_others_categories(categorie) {
  /* be warned that *= will match 'roman', 'romancier', 'roman
     anticipation', 'romance' **/
 Ember.$("div[class='anthologie']")
 .not("div[data-categories*='" + categorie + "']").hide(); 
 Ember.$("div[data-categories*='" + categorie + "']").appendTo('main'); 
}

function remove_current_filter_menu(array_filter_menus, current_filter_menu) {
  let these_filter_menus = array_filter_menus.filter(function(elem) {
  return elem != current_filter_menu; 
 });
 // console.log(these_filter_menus);
 return these_filter_menus;
}

function set_context(every_filters_menu, elem, menu_id, categorie) {
  if ( Ember.$(elem).hasClass('active') ) {
    Ember.$("div[class='anthologie']").show();
    deactivate_other_filter_menus(
      remove_current_filter_menu(every_filters_menu, menu_id) );
    hide_others_categories(categorie);
  } else {            
    Ember.$('div[class="anthologie"]').show();
  }
}

export {deactivate_other_filter_menus, remove_current_filter_menu, set_context}

And I used this import in the component so I was able to use the exported functions from the module/util by prefixing them with “filtres_utils” like “filtres_utils.deactivate_other_filter_menus”.

// app/components/filtres-uneq.js

import Ember from 'ember';
import * as filtres_utils from '../utils/filtres-communs';

export default Ember.Component.extend({
  actions: {
    filtrer(filtre, event) {
      let elem = Ember.$(event.target);
      // we want to get the button tag, but we can get the h2 tag instead
      // wich is inside the button tag
      if ( elem.is('h2') ) { elem = Ember.$(elem).parent(); }
      const all_filters_menu = ['decouvrir', 'nouvelles', 'roman', 'epistolaire', 'recit',
                                'poesie', 'essai-etude', 'conte', 'fragments',
                                'journal_litteraire', 'bande-dessinee'];
      switch (filtre) {
        case 'decouvrir':
          // this is a special case
          // see data-decouvrir that can be set for all elem to Oui/Non
          // the other cases (see below) handle data-categories
          Ember.$(elem).toggleClass('active');
          if ( Ember.$(elem).hasClass('active') ) {
            filtres_utils.deactivate_other_filter_menus(
             filtres_utils.remove_current_filter_menu(all_filters_menu, 'decouvrir') );
            Ember.$("div[data-decouvrir='Non']").hide();
            Ember.$("div[data-decouvrir='Oui']").appendTo('main');
          } else {            
            Ember.$('div[class="anthologie"]').show();
          }
          break;
        case 'nouvelles':
          Ember.$(elem).toggleClass('active');
          filtres_utils.set_context(all_filters_menu, elem, 'nouvelles', 'Nouvelle');
          break;
(...)

Cheers

-Emmanuel


#4

Seems like you’ve already found something that works for you.

But generally speaking, Mixins and custom “utils” directory like you have done are the most common practices from my own experience and observation.