jQuery – 4. del – privzete nastavitve

December 12th, 2009 by Krof Drakula Leave a reply »

Kot sem obljubil v prejšnjem članku, se bomo tokrat osredotočili na tehniko nastavljanja in uporabe privzetih parametrov v lastnih vtičnikih. Za uspešno razumevanje tega članka priporočam branje drugega in tretjega dela v seriji, lahko pa začnete z branjem prvega članka v seriji.

Nastavljanje privzetih nastavitev znotraj našega plugina je odvisno od tega, kakšen tip vtičnika želimo napisati – statične metode na jQuery objektu ali metode na množici elementov.

V prvem primeru si oglejmo preprost primer debug funkcije, ki jo želimo s pomočjo privzetih nastavitev konfigurirati:

(function($) {
  $.extend({
    debug: function(message) {
      if(console && console.debug) {
         console.debug(message);
      } else {
         alert(message);
      }
    }
  });
})(jQuery);

S tem smo dodali preprosto funkcijo, imenovano debug, na sam jQuery objekt, ki jo lahko kličemo na naslednji način:

$.debug("Lorem ipsum");

S tem klicem preprosto uporabimo console.debug ali alert funkcijo, če prve ni na voljo. Vendar je pa funkcija namenoma napisana zelo naivno; ob vsakem klicu na $.debug preveri obstoj funkcije znotraj if stavka, kar ob primerno velikem številu klicev lahko upočasni aplikacijo. Priznam, v konkretnem primeru je to praktično nemogoče doseči, ampak če bi se ta klic izvajal par tisočkrat, bi se pa ta dodatniif stavek poznal pri času izvajanja.

Skratka, želimo se izogniti preverjanju, zato bomo kazalec na funkcijo shranili v spremenljivki, ki jo bomo nastavili znotraj našega plugina:

(function($) {
  var debugFunction = (console && console.debug)? console.debug : alert;
  $.extend({
     debug: function(message) {
       debugFunction(message);
     }
  });
});

S tem smo premaknili if stavek izven funkcije ter jo klicali v sami $.debug funkciji (s pomočjo ternarnega pogojnega operatorja).

Zakaj in kako to še vedno deluje? Če slučajno tega še niste vedeli, se funkcije obnašajo tako kot vsi ostali objekti v Javascriptu – imajo lastnosti, ki so lahko drugi objekti, primitivne vrednosti ali druge funkcije. Če spremenljivki podamo ime funkcije, s tem podamo referenco na to funkcijo, spremenljivko samo pa lahko tudi pokličemo, kot bi klicali prvotno funkcijo, kot orisuje naslednji primer:

var a = function(msg) { alert(msg); };
var b = a;
b("Hello world!");

Isti princip sem uporabil tudi v prejšnjem primeru, kjer sem spremenljivki nastavil vrednost funkcije, ki ji bomo podali sporočilo.

Je pa še en primer, kjer pa naš način ne zadovoljuje potrebam uporabnika – kaj pa če uporabljamo kako drugo orodje, ki omogoča zbiranje debug sporočil in bi želeli uporabiti to funkcijo?

Ena ideja je, da enostavno podamo funkcijo kot enega izmed parametrov, ampak to že po par klicih postane hipoma nadležno, nepotrebno in celo omejujoče – kaj, če se tekom razvoja odločimo zamenjati debugging orodje? V takšnem primeru bi morali opraviti refactoring na celotni kodi, da bi odstranili in/ali zamenjali referenco. Obstajata tudi dve drugi rešitvi. Pri prvi deklariramo novo spremenljivko, ki nosi referenco na željeno funkcijo in jo podajamo $.debug funkciji; tako bi lahko z eno spremembo vplivali na celotno kodo, ampak je nerodna, če delamo z več ločenimi deli kode v več različnih datotekah. Druga rešitev je pa bolj neposredna – popravimo kodo plugina. Slednja je tudi najmanj zaželjena, saj uporabnikov nočemo prisiliti v spremembe v naši kodi, ker potem postane nadgradnja nemogoča brez posegov v vsako novo kopijo.

Edina sprejemljiva možnost v tem primeru je to, da spremenljivko, ki drži referenco na našo debug funkcijo (debugFunction), naredimo vidno vsem. Prva ideja je uvedba kakšne globalne spremenljivke, torej premik debugFunction izven funkcije v globalni imenski prostor, ampak se temu želimo izogniti, ker je to slaba praksa. Druga ideja pa se nanaša na prej omenjeno lastnost funkcij znotraj Javascripta.

Ker lahko obravnavamo vsako funkcijo kot objekt, bomo funkciji pripeli tudi objekt, ki bo držal vrednosti naših parametrov. V tem primeru bomo opravili refactoring na naši kodi:

(function($) {
  $.extend({
    debug: function(msg, params) {
       var p = $.extend({}, $.debug.defaults, params);
       p.debugFunction(msg);
    }
  });
  $.debug.defaults = {
    debugFunction: (console && console.debug) ? console.debug : alert
  };
})(jQuery);

Na prvi pogled zgornja koda izgleda mogoče malce nerazumljiva, zato pojdimo korak za korakom:

  1. Najprej razširimo jQuery objekt s statično metodo debug, ki prejme dva parametra – msg in params. Prvi drži isto vsebino kot naša prvotna funkcija, drugi parametere pa drži parametre, podane kot lastnosti objekta.
  2. Spremenljivka p drži rezultat extend funkcije, ki vrednosti $.debug.defaults povozi z vrednostmi iz params objekta.
  3. Ko izvedemo $.extend funkcijo za debug, nastavimo na $.debug novo lastnost, imenovano defaults. Tu definiramo vse parametre, ki jih želimo nastaviti kot privzete. Ta lastnost je tudi vidna navzven, saj lahko vsak nato spreminja njeno vrednost s klicem na $.debug.defaults.

Sedaj imamo možnost klicati lastno funkcijo na več različnih načinov:

// izvede funkcijo s privzetimi nastavitvami
$.debug("Lorem ipsum");
// eksplicitno poda referenco na alert
$.debug("Lorem ipsum", { debugFunction: alert });

Poleg teh dveh smo pa s privzetimi nastavitvami omogočili tudi, da uporabnik privzete nastavitve tudi spremeni:

$.extend($.debug.defaults, { debugFunction: alert });
// od te točke naprej je funkcija alert
$.debug("Lorem ipsum");

Pri tem moramo seveda paziti, da kot uporabniki plugina vedno uporabljamo $.extend, ko nastavljamo defaults lastnost, če nastavljamo več vrednosti oz. vrednosti nastavljamo vedno eksplicitno:

$.debug.defaults.debugFunction = alert;

Če bi želeli neposredno spremeniti defaults lastnost na nov objekt, bi morali zagotoviti, da zmeraj nastavimo vse vrednosti, ki so bile prej nastavljene; manjkajoče vrednosti bi lahko povzročale napako, ko bi plugin želel uporabiti vrednost neobstoječe lastnosti, pri čemer bi priklic takšne lastnosti vrnil undefined.

Zdaj si pa poglejmo še primer iz drugega članka v tej seriji:

(function($) {
  $.extend({
    alternateRows: function() {
      return $(this).each(function() {
        if($(this).is(":table")) {
          $(this).find("tr:even").addClass("even");
          $(this).find("tr:odd").addClass("odd");
        }
      });
    }
  });
})(jQuery);

Kot hiter povzetek – funkcija iterira po vsakem izmed elementov v množici rezultatov in preveri, če je trenutni element tabela. Če se to izkaže za resnično, izbere vsa liha polja in se aplicira klaso odd, v nasprotnem primeru pa even.

Konfiguracijo smo takrat zastavili tako, da smo morali eksplicitno podati dva parametra, ki predstavljata imeni even in odd klas, ampak, kot smo že rekli, je eksplicitno podajanje parametrov nerodno in nepotrebno, zato bomo preoblikovali funkcijo tako, da bomo kot argument podali objekt z lastnostmi in privzete vrednosti nastavili na podoben način kot v prejšnjem primeru:

(function($) {
  $.fn.extend({
    alternateRows: function(params) {
      var p = $.extend($.fn.alternateRows.defaults, params);
      return $(this).each(function() {
        if($(this).is(":table")) {
          $(this).find("tr:even").addClass(p.even);
          $(this).find("tr:odd").addClass(p.odd);
        }
      });
    }
  });
 $.fn.alternateRows.defaults = { even: "even", odd: "odd" };
})(jQuery);

S tem smo uspešno spremenili funkcijo na podoben način kot s statično metodo, pri čemer so vsi naslednji klici pravilni:

// uporabi privzete nastavitve
$("table").alternateRows();
// povozi even parameter
$("table").alternateRows({ even: "evenClass" }); 
 
// povozi odd privzeti parameter
$.extend($.fn.alternateRows.defaults, { odd: "oddClass" }); 
// uporablja novo privzeto vrednost za odd
$("table").alternateRows();

S tem člankom smo na kratko pokrili konfiguracijo in podajanje parametrov za lastne jQuery plugine. Teme za naslednji članek še nisem izbral, naj pa razveselim vse voljne bralce, da vas čaka še vsaj par člankov v tej seriji. :) Če ima pa kdo kakšno idejo ali željo glede razvoja jQuery pluginov, pa le na dan z besedo v komentarjih.

Advertisement

Comments are closed.