2011-09-30 12 views
10

के साथ प्रोग्रामेटिक रूप से कॉलबैक लागू करना तो, मैं एक वेब ऐप लिख रहा हूं। बहुत कुछ सब कुछ क्लाइंट-साइड किया जाता है, सर्वर एक रीस्टफुल इंटरफ़ेस है। मैं अपनी पसंद के ढांचे के रूप में jQuery का उपयोग कर रहा हूं और Revealing Module Pattern में अपना कोड कार्यान्वित कर रहा हूं। ,जेएस/jQuery

(function($){ 
    $.fn.myplugin = function(method) 
    { 
     if (mp[method]) 
     { 
      return mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); 
     } 
     else if (typeof method === 'object' || ! method) 
     { 
      return mp.init.apply(this, arguments); 
     } 
     else 
     { 
      $.error('Method ' + method + ' does not exist on $.myplugin'); 
     } 
    }; 

    var mp = 
    { 
     init : function(options) 
     { 
      return this.each(function() 
      { 
       // stuff 
      } 
     }, 
     callbacks : {}, 
     addCallback : function(hook_name, cb_func, priority) 
     { 
      // some sanity checking, then push cb_func onto a stack in mp.callbacks[hook_name] 
     }, 
     doCallbacks : function(hook_name) 
     { 
      if (!hook_name) { hook_name = arguments.callee.caller.name; } 
      // check if any callbacks have been registered for hook_name, if so, execute one after the other 
     } 
    }; 
})(jQuery); 

सुंदर सीधा सही:

मेरी कोड के wireframe मूल रूप से इस तरह दिखता है?

अब, हम अंदर से और साथ ही एप्लिकेशन स्कोप के बाहर से (एकाधिक, पदानुक्रमित) कॉलबैक पंजीकृत करने में सक्षम हैं।

मुझे क्या गुस्सा दिलाना है:

foo : function() { 
    mp.doCallbacks('foo_before'); 
    // do actual stuff, maybe some hookpoints in between 
    mp.doCallbacks('foo_after');   
} 

मेरे ऐप के अंदर हर एक समारोह प्रारंभ और समाप्ति करने के लिए होगा: पूरी बात संभव के रूप में विस्तृत बनाने के लिए, मैं इन पंक्तियों के साथ कुछ का सहारा लेना होगा उसके जैसा। यह सिर्फ सही प्रतीत नहीं होता है।

तो, एसएस के जेएस जादूगर - क्या करते हैं?

+0

लगभग हर प्लगइन वास्तुकला मैं कार्यान्वित देखा गया है से पहले करने के लिए और हुक के बाद सहारा लिया:

यहाँ एक उदाहरण कोड है। जब तक मैं कह सकता हूं, ज्यादातर समय आप केवल उन लोगों को लागू करते हैं जो लोग इसके लिए पूछते हैं। कोई जादू समाधान जो मुझे पता है। –

+0

यह ओवर-इंजीनियरिंग के मामले की तरह लगता है। कॉलबैक से पहले और बाद में समर्थन करने के लिए क्या आपको वास्तव में अपनी लाइब्रेरी में प्रत्येक _internal_ फ़ंक्शन की आवश्यकता है? रेल एक विशाल कॉलबैक सैंडविच की तरह है, और फिर भी, रेल में केवल 1% विधियों का समर्थन कॉलबैक है। और, जहां वे समर्थन कॉलबैक करते हैं, वे वैसे ही करते हैं जैसा आपने अपने उदाहरण _foo_ फ़ंक्शन में उल्लिखित किया है। –

उत्तर

10

आप एक ऐसा फ़ंक्शन लिख सकते हैं जो तर्क के रूप में एक और कार्य करता है, और एक नया फ़ंक्शन देता है जो उस तर्क के आस-पास आपके हुक को कॉल करता है। उदाहरण के लिए:

foo: withCallbacks("foo", function() { 
    // Do actual stuff, maybe some hookpoints in between. 
}) 
+0

+1 मैंने आपके पोस्ट को संपादित करने से पहले लगभग कुछ कहा, अच्छा समाधान हालांकि! – Chad

+0

@ चाड, धन्यवाद, मुझे यह स्वीकार करने में कुछ समय लगा कि मैं फ़ंक्शन से हुक नामों को कम नहीं कर सकता :) –

+0

मेरे 'doCallbacks() 'कार्यान्वयन में, यदि कोई' hook_name' नहीं दिया गया है, तो मैं 'तर्क का उपयोग करता हूं। callee.caller.name' को फ़ंक्शन से हुक नाम को कम करने के लिए ... यदि कॉल के साथ कॉल किया जाता है तो स्पष्ट रूप से टूटा जाएगा; – vzwick

2

आप अनिवार्य रूप से jQueryUI Widget factory की एक छीन नीचे संस्करण को लागू कर रहे हैं:

function withCallbacks(name, func) 
{ 
    return function() { 
     mp.doCallbacks(name + "_before"); 
     func(); 
     mp.doCallbacks(name + "_after"); 
    }; 
} 

तो फिर तुम जैसे कुछ लिख सकते हैं। मैं इसे स्वयं रोल करने से बचने के लिए उस कार्यक्षमता का उपयोग करने की सलाह दूंगा।

$("#foo").myPlugin("myMethod", "someParam") 

'someParam' एक तर्क के रूप के साथ प्लगइन उदाहरण पर myMethod कॉल करेगा:

विजेट कारखाने ऑटो जादुई विधि कॉल करने के लिए तार, जैसे कि नक्शा होगा। इसके अतिरिक्त, यदि आप एक कस्टम ईवेंट को आग लगाते हैं, तो उपयोगकर्ता ईवेंट नाम से मेल खाने वाले विकल्पों में एक संपत्ति जोड़ कर कॉलबैक जोड़ सकते हैं।

उदाहरण के लिए, tabs विजेट एक select घटना है कि आप प्रारंभ दौरान विकल्प के लिए एक select संपत्ति जोड़कर में शामिल हो सकता है:

$("#container").tabs({ 
    select: function() { 
     // This gets called when the `select` event fires 
    } 
}); 
निश्चित रूप से

, आप पहले और बाद में जोड़ने की आवश्यकता होगी घटनाओं के रूप में हुक इस कार्यक्षमता को उधार लेने में सक्षम होने के लिए, लेकिन यह अक्सर किसी भी तरह से आसान रखरखाव की ओर जाता है।

उम्मीद है कि मदद करता है। चियर्स!

+0

सूचक के लिए धन्यवाद, इसके बारे में पता था, लेकिन वैसे भी;) – vzwick

4

मैं सवाल का सही ढंग से समझ नहीं हो सकता है, क्योंकि मैं तुम क्यों myplugin कोड में सीधे कॉलबैक कॉल करने के लिए कोड जोड़ नहीं है नहीं दिख रहा है:

$.fn.myplugin = function(method) 
{ 
    if (mp[method]) 
    { 
     var params = Array.prototype.slice.call(arguments, 1), ret; 
     // you might want the callbacks to receive all the parameters 
     mp['doCallbacks'].apply(this, method + '_before', params); 
     ret = mp[method].apply(this, params); 
     mp['doCallbacks'].apply(this, method + '_after', params); 
     return ret; 
    } 
    // ... 
} 

संपादित करें:

ठीक है, आपकी टिप्पणी पढ़ने के बाद मुझे लगता है कि एक और समाधान (निश्चित रूप से) एक और संकेत होगा। यही है, एक आविष्कार समारोह है जिसका उपयोग कन्स्ट्रक्टर के साथ-साथ स्वयं के बीच कॉल के लिए अन्य सार्वजनिक तरीकों से किया जा रहा है।मैं यह इंगित करना चाहता हूं कि यह केवल सार्वजनिक तरीकों के लिए ही काम करेगा, क्योंकि निजी तरीकों के हुक ब्रेक इनकैप्यूलेशन को वैसे भी जोड़ते हैं।

सरल संस्करण कुछ इस तरह होगा:

function invoke(method) { 
    var params = Array.prototype.slice.call(arguments, 1), ret; 
    // you might want the callbacks to receive all the parameters 
    mp['doCallbacks'].apply(this, method + '_before', params); 
    ret = mp[method].apply(this, params); 
    mp['doCallbacks'].apply(this, method + '_after', params); 
} 

$.fn.myplugin = function() { 
    // ... 
    invoke('init'); 
    // ... 
}; 

लेकिन, मैं वास्तव में थोड़ा अधिक कोड, कि प्लग-इन के बीच दोहराव के रूप में अच्छी तरह से कम होगी लिखा है। यह एक प्लगइन बनाने अंत

(function() { 

function getResource() { 
    return {lang: "JS"}; 
} 

var mp = NS.plugin.interface({ 
    foo: function() { 
     getResource(); // calls "private" method 
    }, 
    bar: function() { 
     this.invoke('foo'); // calls "fellow" method 
    }, 
    init: function() { 
     // construct 
    } 
}); 

$.fn.myplugin = NS.plugin.create(mp); 

})(); 

में दिखाई देंगे है और यह कैसे आंशिक कार्यान्वयन की तरह लग रहा है:

NS = {}; 
NS.plugin = {}; 

NS.plugin.create = function(ctx) { 
    return function(method) { 
     if (typeof method == "string") { 
      arguments = Array.prototype.slice.call(arguments, 1); 
     } else { 
      method = 'init'; // also gives hooks for init 
     } 

     return ctx.invoke.apply(ctx, method, arguments); 
    }; 
}; 

// interface is a reserved keyword in strict, but it's descriptive for the use case 
NS.plugin.interface = function(o) { 
    return merge({ 
     invoke:  NS.plugin.invoke, 
     callbacks: {}, 
     addCallback: function(hook_name, fn, priority) {}, 
     doCallbacks: function() {} 
    }, o); 
}; 

NS.plugin.invoke = function(method_name) { 
    if (method_name == 'invoke') { 
     return; 
    } 

    // bonus (if this helps you somehow) 
    if (! this[method]) { 
     if (! this['method_missing') { 
      throw "Method " + method + " does not exist."; 
     } else { 
      method = 'method_missing'; 
     } 
    } 

    arguments = Array.prototype.slice.call(arguments, 1); 

    if (method_name in ["addCallbacks", "doCallbacks"]) { 
     return this[method_name].apply(this, arguments); 
    } 

    this.doCallbacks.apply(this, method_name + '_before', arguments); 
    var ret = this[method_name].apply(this, arguments); 
    this.doCallbacks.apply(this, method_name + '_after', arguments); 

    return ret; 
}; 

बेशक, यह पूरी तरह से अपरीक्षित है :)

+0

यह बाहरी रूप से/"सार्वजनिक" विधियों के लिए व्यवहार्य होगा। मुझे कॉलबैक अभी भी निष्पादित करने के साथ "अंदर" 'mp' से 'this.someFunction()' करने में सक्षम होना चाहिए। – vzwick

+0

मैंने अपने संपादन में आपकी टिप्पणी को संबोधित किया है। आपके उपयोग के मामले के लिए बहुत जटिल हो सकता है, लेकिन लिखना मजेदार था। – deviousdodo

+0

कोड का साफ टुकड़ा! – vzwick

1

मूल रूप से मैं कॉलबैक से बचने और इसके बजाय घटनाओं का उपयोग करना पसंद करता हूं। कारण समान है। मैं दिए गए ईवेंट को सुनने के लिए एक से अधिक फ़ंक्शंस जोड़ सकता हूं, मुझे कॉलबैक पैरामीटर के साथ गड़बड़ नहीं करना है और मुझे यह जांचना नहीं है कि कॉलबैक परिभाषित किया गया है या नहीं। जहां तक ​​आपकी सभी विधियों को $.fn.myplugin के माध्यम से बुलाया जाता है, विधि कॉल से पहले और बाद में ईवेंट ट्रिगर करना आसान होता है।

(function($){ 
    $.fn.myplugin = function(method) 
    { 
     if (mp[method]) 
     { 
      $(this).trigger("before_"+method); 
      var res = mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); 
      $(this).trigger("after_"+method); 
      return res; 
     } 
     else if (typeof method === 'object' || ! method) 
     { 
      return mp.init.apply(this, arguments); 
     } 
     else 
     { 
      $.error('Method ' + method + ' does not exist on $.myplugin'); 
     } 
    }; 

    var mp = 
    { 
     init : function(options) 
     { 
      $(this).bind(options.bind); 
      return this.each(function() 
      { 
       // stuff 
      }); 
     }, 

     foo: function() { 
      console.log("foo called"); 
     } 
    }; 
})(jQuery); 


$("#foo").myplugin({ 
    bind: { 
     before_foo: function() { 
      console.log("before foo"); 
     }, 

     after_foo: function() { 
      console.log("after foo"); 
     } 
    } 
}); 
$("#foo").myplugin("foo"); 

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^