General developer forum

Mustache Renderer Javascript/Ajax

 
Picture of Mathieu Domingo
Mustache Renderer Javascript/Ajax
 

Hello,

I'm doing some changes to the block course_overview and I don't understand how to say to re render the block (or ideally only the line but how I understand it after readings all the docs pages and how it's coded, it's all the block that will be re rendered i think).

I want to add an icon on each course line to allow to change the visibility of this course :  I need to change the visibility and then re render the block/line to let the user know that the course visibility has change.

What I made step by step :

Step 1 : In main.php -> process_tab, I added data for the mustache file depending on the visiblity of the course (and also checking for capabilities)

if (has_all_capabilities(array('moodle/course:visibility', 'moodle/course:viewhiddencourses'), \context_course::instance($course->id))) {
    $course->hasvisibilitycapabilities=true;
    if($course->visible){
        $course->visibilityicon="fa-eye-slash";
        $course->visibilityalt=get_string('hide');
        $course->visibilityaction='hide';
    }
    else{
        $course->visibilityicon="fa-eye";
        $course->visibilityalt=get_string('show');
        $course->visibilityaction='show';
    }
}

Step 2 : In courselist.mustache I added an icon to change the visibility of a course :
{{# hasvisibilitycapabilities}}
                        <i class="icon fa fa-fw {{ visibilityicon }} visibility_icon" title="{{ visibilityalt }}" data-id="{{id}}" data-action="{{visibilityaction}}"></i>
{{/ hasvisibilitycapabilities}}

Step 3 : In main.mustache I added a call to a javascript script :
    {{# js }}
        require(['jquery', 'block_course_overview/changevisibility'], function($, Changevisibility) {
            Changevisibility.init();
        });
    {{/ js }}

Step 4 : I created a javascript file named changevisibility.js to detect click on the icon, then call a php file (created at step 5) to change the visibility of the course and then to re render the block.
define(['jquery', 'jqueryui', 'core/config' , 'core/templates', 'core/ajax'], function($, UI, mdlconfig, templates, ajax) {

    return {
        init: function() {

            $(".visibility_icon").click(function () {
               
                //step 4.1 : change the visibility
                var data = {
                        sesskey : M.cfg.sesskey,
                        action : $(this).data('action'),
                        id : $(this).data('id')
                    };
                $.post(
                    M.cfg.wwwroot + '/blocks/course_overview/change_visibility.php',
                    data
                );
       
                //step 4.2 : do something to re render the template now the course visibility have change (any way to change only the line? how it's done, look like it will change the full block)
                //from the docs, it should look something like that I think, but I have no idea how I'm supposed to get the "new_data" ...
               
        //        templates.render('block_course_overview/main', new_data).done(function(html,js){
        //            $('data-region="course-overview"').replaceWith(html);
        //            templates.runTemplateJS(js);
        //        });
       
                //Step 4.2 BIS : dumb solution to change the line -_-
               
                if($(this).data('action')=='hide')
                {
                    $(this).data('action','show');
                    $(this).attr('data-action','show');
                    $(this).addClass('fa-eye').removeClass('fa-eye-slash');
                    $(this).parent().next().children(":first").children(":first").toggleClass("dimmed");
                }
                else if($(this).data('action')=='show')
                {
                    $(this).data('action','hide');
                    $(this).attr('data-action','hide');
                    $(this).addClass('fa-eye-slash').removeClass('fa-eye');
                    $(this).parent().next().children(":first").children(":first").toggleClass("dimmed");
                }
            });
        }
    };
});

Step 5 : I created a php file named change_visibility.php to change the visibility of a course (this file is called after click on item through javascript file created at step 4)
define('AJAX_SCRIPT', true);

require_once(__DIR__ . '/../../config.php');
require_once(__DIR__ . '/locallib.php');
require_once($CFG->dirroot.'/lib/coursecatlib.php');
require_once($CFG->dirroot.'/course/lib.php');

require_sesskey();
require_login();

$action = required_param('action', PARAM_ALPHA);
$courseid = required_param('id', PARAM_INT);

if ($courseid) {
    $record = get_course($courseid);
    $course = new \course_in_list($record);
}

switch ($action) {
    case 'show' :
        \core_course\management\helper::action_course_show($course);
        break;
    case 'hide' :
        \core_course\management\helper::action_course_hide($course);
        break;
}

That could be perfect if someone could help me to understand what i'm supposed to do for step 4.2 to work smile
I don't know if I just need to find what(and how) i have to put in new_data, or if I'm completely wrong...

If it's not clear enough, I just made a fork of the github project here : https://github.com/MathieuDomingo/moodle-block_course_overview
(I'm sorry it contains a few more changes than just this one ..) 

 
Average of ratings: -
Picture of Mathieu Domingo
Re: Mustache Renderer Javascript/Ajax
 
I think i walk one step closer to the solution : regroup end of part 4.1 with 4.2 to get this :
$.post(
    M.cfg.wwwroot + '/blocks/course_overview/change_visibility.php',
    data
    ).done(function(new_data){
        //step 2 : do something to re render the template now the course visibility have change (any way to change only the line? how it's done, look like it will change the full block)
        //should look something like that I think, but I have no idea how I'm supposed to get the "new_data" ...
        templates.render('block_course_overview/main', new_data).done(function(html,js){
        $('[data-region="course-overview"]').replaceWith(html);
        templates.runTemplateJS(js);
     });
});

For test purpose, at the end of step 5,  I added
echo json_encode(array("test"=>"return"));

It's working, the course list go away. Now I need to find how I can fill this array to get the list with new values.

I suppose I need to call the public function export_for_template(renderer_base $output) of file classes/output/main.php but i don't understand how to do it / how it's working..
Is there a way to get an already instantiated object to call this method ? Or should I instantiate a new one ?
What is the renderer_base $output variable ?
 
Average of ratings: -
Picture of Mathieu Domingo
Re: Mustache Renderer Javascript/Ajax
 

After becoming crazy many times reading/trying, i finally made another step after looking more carefully the html code...

So, in javascript file, in var data , to get the instanceid of the block, i added this :
block_instanceid :$('[data-block="course_overview"]').data('instanceid')

When i call my php file with ajax, to get this value and then get the instance of this object :
$block_instanceid = required_param('block_instanceid', PARAM_INT);
$blockinstance= block_instance_by_id($block_instanceid);

(it's so easy when it's written but it took me so long to write this 3 lines -_- )


This allow me to call function get_content()
$blockinstance->get_content();

The problem is this function already call a renderer and return me the text already rendered... So i can't give it to  templates.render in my javascript file ..
At this step, if I return it to the javascript, like this :
echo json_encode($blockinstance->get_content());

I can do this in the javascript file :
templates.render('block_course_overview/main',new Object()).done(function(html,js){
    $('[data-region="course-overview"]').replaceWith(new_data.text);
    templates.runTemplateJS(js);
});

It's "working", but it doesn't look like the normal code like in the docs (seems a bit hacky i don't like it)

Looking more closely the code of get_content(), it's seems i need to change just the end of the function to get the return I need.

I tried to make another function and change the end of get_content like this :
$renderer = $this->page->get_renderer('block_course_overview');
$main = new block_course_overview\output\main($config, $tabs, $isediting, $tab, $sortorder, $favourites);
return $main->export_for_template($renderer);

Then I call it like this in my php ajax_script :

$test= $blockinstance->get_content_to_render();

The problem is $test contain all the good values expect all the links , which are all empty..

I tried to look values in process_tab, it's look ok, but if I look in export_for_template it's not.

If anyone have any idea of what happens, thanks to say me because i don't have any :'(

(If it's not clear, I actually updated the github fork )

 
Average of ratings: -
Picture of Mathieu Domingo
Re: Mustache Renderer Javascript/Ajax
 

Finally used the "hacky version" .. 

Seems to work nicely smile  (Can change visiblity and favourite course without reloading , and it also work if javascript is deactivated)

 
Average of ratings: -
Picture of Jean-Roch Meurisse
Re: Mustache Renderer Javascript/Ajax
Core developersPlugin developersTesters

Hi Mathieu,

Which moodle version are you using? Which theme? 

Be careful with course_overview block. I think it will be removed from core in 3.7 (but maybe I am wrong).

It is clearly possible to add that "show/hide" function in myoverview block, even if it's a little more tricky with 3.6 than with 3.5. 

So if you want to implement that in myoverview rather than course_overview, come back to me (in French if you like wink ) and I'll give you hints



 
Average of ratings: -
Picture of Mathieu Domingo
Re: Mustache Renderer Javascript/Ajax
 

Hi JR

I'm using Moodle 3.5 with theme adaptable.

If I have understand well, they have already removed it from the core (that's why now it's a plugin), and it probably going to stop working in 3.7
The problem is our users really don't like the "new" (introduced in 3.3 ?) myoverview so we kept the "old" one while we can and we will change when it's will no longer work.

From my point of view, It's "finished" now, everything seems to work nicely (with and without javascript)
To make it really clean, I should have try to change more code and i don't have enough time for it now (and this code is probably going to garbage in something like 1 or 2 year, so it's not worth to spend more time on this just to go around an "hacky" step (a working one actually))

 
Average of ratings: -