Need further help with developing first plugin

  • NodeBB

    Don't give up so easily 馃檪

    All the filters take a callback parameter which you can call after you are done modifying the data.

    In your case it would look something like this

    MyPlugin.myMethod = function(categoryObj, callback) {
        // do something with the data 
        categoryObj.category.homepageSection = 1;
        console.log(categoryObj);
        callback(null, categoryObj);
    };
    

    The first param is an error object and then you pass the data that you modified back into the callback so other plugins that listen for the same hook can keep on modifying the data.

  • GNU/Linux

    baris said:

    Don't give up so easily 馃檪

    Yes! Yes! Yes! 馃檪

    JavaScript is a prototypal language, which makes it a bit confusing, but very powerful. You can structure your programs in various ways. I'm only gonna go into how it's (mostly) done in nodeJS:

    Although you define objects with members, it is uncommon to actually create instances of those objects. JS has a new keyword that allows you to do that, but there it gets messy with this and what it means. Sometimes this points to an object, sometimes a function - it all depends on where you use it and who called what. You should read up on 'function scope' and 'closure', in case you're interested. I myself still like to use it. But anyway, most code you will encounter treats objects as static. Also, function parameters are passed by value rather than by reference (you may know this from C as passing a variable vs. passing a pointer). Let's have a look:

    var aNumberVariable = 50;
    
    myMethod = function(functionsNumberVariable) {
        functionsNumberVariable = 100;
        console.log(functionsNumberVariable);
    };
    
    myMethod(aNumberVariable);
    console.log(aNumberVariable);
    

    When you execute this snippet, the output will read 100 and then 50.

    Your browser's console is awesome for fiddling! I use chrome. 馃檪

    byval-byref.png

    Here you can see, that aNumberVariable is assigned 50, myMethod defined and then called. The parameter is passed to the function, where only its value is received, and put into a newly created variable functionsNumberVariable. This then is set to 100 and written to the console. The function call (myMethod()) is done synchronously, so everything following 'waits' for the function to finish/return. It does so with no value (aka undefined), but the console ignores that. Then the last line is executed, showing that aNumberVariable's value is still 50. (The showing undefined in the picture is actually not from anywhere in my code. I think that is the result of a function, the console's VM wraps your statements in. Once again the scope thingy.)

    But this also means, that once myMethod is through doing its thing, you have no way to access functionsNumberVariable anymore (before mentioned closure or function scope). Even worse, it's a candidate to be gobbled up by the garbage collector in one of its next runs. Now, you could, of course, easily just return its value, but this is where nodeJS's magic of asynchronicity gets in between.

    Everything below would have to wait for your function to return, so a read from a database or from a network resource could easily block your process for a lengthy amount of time - not so good.
    Fortunately, JavaScript is capable of passing functions as parameters. 馃憤. This - we're almost there - is the callback (in our case here; you can do heaps of other neat things with it). You basically pass the next step in your chain of processing as this function, passing it the values which you otherwise would have returned. I fail to come up with a quick and easy code example to run, right now. So I'll draw it in a diagram:
    Untitled (3).png
    At this point you'd be perfectly right to say: "Well, nice story. But there's also an invisible return in queryDatabase which the program has to wait for.". Yes, it is. That's why I painted its declaration yellowish. Your JavaScript runs also in nodeJS on a single thread, but under the hood, when V8 (JS engine) passes such calls to the I/O layer it utilises multi-threading. This game of passing callbacks is played along the chain, until V8 'queries' some system I/O function. There it is split off into another thread, from where (on the return path, if you will) the callbacks are called one after another. The actual functions returns are done as soon V8 has branched off this new thread. I'm not 100% sure about this last part, though. I urge everyone who knows more about this to correct me! 馃檪

    By the way: Every function comes with several 'invisible' variables set. One of which is arguments. That's an array of all parameters passed to the function. Disregarding the declarations (function(var1, var2)) you did. So even when you define your function as myMethod = function(), arguments is available and filled with everything that has been passed in the call. Great for exploring! 馃檪
    arguments.png

  • Community Rep

    I do hope @Joel-Murphy comes back and keeps at it.


  • @baris

    Ah thank you, I was so close 馃憤

    @rbeer

    Wow thank you so much for your long, detailed post. I decided I needed a refresher on JS after reading your post, so that's where I've been for the past few days. I completely forgot about closures, but now I think I'm back up to scratch with them so things should make a lot more sense now 馃槃

    @BDHarrington7

    Oh I will don't worry! 馃憤 I've already started translating NodeBB to Welsh and am keen to get good at plugin development.


  • One thing I'm still unsure about though is how logic works in templates. Without logic inside of statements, doesn't it make it near impossible to do anything more than show or hide certain elements?

    Assume I want to order (or more precisely, group) categories on the homepage, how should I do this?

    <ul class="categories" itemscope itemtype="http://www.schema.org/ItemList">
    	<p>A few threads:</p>
    
    	<!-- BEGIN categories -->
    	{categories.homepage_section}
    	put categories with a homepage_section value of '1' here.
    	<!-- IMPORT partials/categories/item.tpl -->
    	<!-- END categories -->
    
    	<li class="line-seperator"></li>
    
    	<p>Other threads:</p>
    
    	<!-- BEGIN categories -->
    	{categories.homepage_section}
    	put categories with a homepage_section value of '2' here.
    	<!-- IMPORT partials/categories/item.tpl -->
    	<!-- END categories -->
    </ul>
    

    This should produce something like this:

    A few threads

    • Test1 (1)
    • Test2 (1)
    • Test3 (1)

    Other threads

    • Test1 (2)
    • Test2 (2)
    • Test3 (2)

    Anyone have any ideas? The only obvious solution I can think of right now is adding attributes such as homepage_section1 = true and homepage_section2 = false to each category. But this seems very long winded and hardly efficient.

    Thanks


  • Okay, after more fiddling around, I've figured out even more about writing plugins. I've now written some code using client side javascript to group each category together according to its homepageSection id. Now I just need to figure out how to render the page after asynchronously rendering each category item.

    $(document).ready(function() {
        
        console.log('Client side JS loaded');
    
        $(window).on('action:ajaxify.contentLoaded', function(ev, data) {
    		if (data.tpl === 'categories') {
    			
    			var sections = {};
    			var sectionNames = ['A few threads', 'Other threads'];
    			var categoriesHTML = "";
    
    			// Loop over each category and assign them to the section array based on their homepageSection value.
    			for(var i = 0; i < ajaxify.data.categories.length; i++){
    				if( (typeof ajaxify.data.categories[i].homepageSection != 'undefined') && (ajaxify.data.categories[i].hasOwnProperty("homepageSection") )){
    					sections["section"+ajaxify.data.categories[i].homepageSection] = sections["section"+ajaxify.data.categories[i].homepageSection] || [];
    					sections["section"+ajaxify.data.categories[i].homepageSection].push(ajaxify.data.categories[i]);
    				}
    			}
    
    			// Loop over each section in the sections array, then render each category in each section
    			for(section in sections){
    				for (var i = 0; i < sections[section].length; i++) {
    					var myData = sections[section][i];
    
    					ajaxify.loadTemplate('partials/categories/item', function(myTemplate) {
    						var html = templates.parse(myTemplate, myData);
    						categoriesHTML += html;
    ;
    						// loadTemplate is asynchronous and so this is where I suck @ node. Time for some more learning...
    					});
    				};
    			}
    			
    		}
    	});
    });
    

    Slow progress, but I'm nearly there 馃槃

  • Community Rep

    @Joel-Murphy

    Get the template first, then parse. 馃檪

    $(document).ready(function() {
    
    	console.log('Client side JS loaded');
    
    	$(window).on('action:ajaxify.contentLoaded', function(ev, data) {
    		if (data.tpl === 'categories') {
    			
    			var sections = {};
    			var sectionNames = ['A few threads', 'Other threads'];
    			var categoriesHTML = "";
    
    			// Loop over each category and assign them to the section array based on their homepageSection value.
    			for(var i = 0; i < ajaxify.data.categories.length; i++){
    				if( (typeof ajaxify.data.categories[i].homepageSection != 'undefined') && (ajaxify.data.categories[i].hasOwnProperty("homepageSection") )){
    					sections["section"+ajaxify.data.categories[i].homepageSection] = sections["section"+ajaxify.data.categories[i].homepageSection] || [];
    					sections["section"+ajaxify.data.categories[i].homepageSection].push(ajaxify.data.categories[i]);
    				}
    			}
    
    			ajaxify.loadTemplate('partials/categories/item', function(myTemplate) {
    
    				// Loop over each section in the sections array, then render each category in each section
    				for(section in sections){
    					for (var i = 0; i < sections[section].length; i++) {
    						var myData = sections[section][i];
    
    						var html = templates.parse(myTemplate, myData);
    						categoriesHTML += html;
    					};
    				}
    
    				// Done parsing, add it.
    				$('.categories').html(categoriesHTML);
    			});
    		}
    	});
    });
    

  • @yariplus

    Oh you're right! Not sure why I was loading the template multiple times! Thanks

    This is the result I get. I'm guessing some further rendering functions need to be called after the template is appended to the DOM?

    Screen Shot 2015-07-24 at 13.44.40.png


  • Another update:

    The rendering is actually working fine.

    The following HTML:

    <small>{../description}</small>
    

    Should be changed to:

    <small>{description}</small>
    

    To make those properties show.
    However, functions within the theme don't appear to be working. E.g.

    <div class="icon hidden-sm hidden-xs pull-left" style="{function.generateCategoryBackground}">
    	<i class="fa fa-fw {../icon}"></i>
    </div>
    

    I'm guessing this is due to the way I'm passing an object to a template when it's being rendered?

    Edit:

    I've tried defining a test helper inside of main.js:

    templates.registerHelper('testingz',function(data, iterator, numblocks) {
        console.log('{function.testingz}');
        return "testingz";
    });
    

    And calling it from my HTML template using {function.testingz} but nothing happens. Am I doing this correctly? As soon I figure this out, I should be able to finish my first plugin 馃槃

  • Community Rep

    Hmm, not sure.Templates is @psychobunny 's thing. Maybe he has the treasure.

Suggested Topics

| |