Jump to content

Chris.Smith

School User
  • Posts

    139
  • Joined

  • Last visited

Posts posted by Chris.Smith

  1. 7 minutes ago, pconkie said:

    @Chris.Smith

    Trying to change from $(selector) to this.element.find(selector)

    Had this which shows week two of a two week timetable (and hides week one)

     

    
    $(".mywrapper #wk1btn").click(function() {
    
      $(".mywrapper #week1").hide();
      $(".mywrapper #week2").show();
    
    )};

    Now i've got this...

    
    var $wk1btn = this.element.find(".mywrapper #wk1btn");
      
    $wk1btn.click(function() {
        var $week1 = this.element.find(".mywrapper #week1");	*******
        	$week1.hide();
        var $week2 = this.element.find(".mywrapper #week1");
      	$week2.show();
    });

    But get an uncaught type error: cannot read property 'find' of undefined on line **** above.

    Any thoughts?

    Ta  Paul

    Hi Paul,

    Remember when I said you probably won't find a need for .bind(this)?! Another alternative would be to "hoist" a variable in.

    var $mywrapper = this.element.find(".mywrapper");
    
    $mywrapper.find('#wk1btn').on('click', function(ev) {
      $mywrapper.find('#week1').hide();
      $mywrapper.find('#week2').show();
    });
    
    // ############################ OR #######################
    var wklbtn = this.element.find(".mywrapper #wk1btn");
    
    $wk1btn.click(function() {
    	var $week1 = this.element.find('.mywrapper #week1'),
            $week2 = this.element.find('.mywrapper #week2');
      
      $week1.hide();
      $week2.show();
    }.bind(this));

    Hope this helps,

     

    ~ Chris

    • Like 2
  2. 4 hours ago, ADT said:

    @Graham Quince @Chris.Smith  I take it you boys are converting this into a Frog Code widget right?

    Well Adrian, I think that is a brilliant idea! If only I knew an ICT teacher and VLE coordinator whose Year 11 and 13s where about to leave school thus freeing up some of his famously busy schedule allowing him to make said widget :P

    Honest answer is we are busy getting some surprises ready in time for conference; so we don't have a great deal of time. 

    @pconkie are you up for a challenge?

    • Like 1
  3. 21 hours ago, mobrien said:

    Hi @Chris.Smith,

    This is much tidier than my original 'attempt'. The organisation of the data rows in the code is fine, and easily manipulated which is what I need.

    There are two parts that I would like to add, a Header row colour for our House Groups (should this go within the Styles?) and the filtering function that you describe.

    Really grateful for all this help!

    Michael

    Hi @mobrien,

    Here is the code transformed to allow filtering. The styling you can add yourself :)

    <style>
        .template {
            display: none;
        }
    </style>
    
    <table class="table table-striped">
        <thead>
            <th>Name</th>
            <th>House</th>
            <th>Tutor</th>
            <th>Achievement</th>
            <th>Behaviour</th>
            <th>Total</th>
        </thead>
        <tbody></tbody>
    </table>
    
    <table class="template">
        <tbody>
            <tr>
                <td data-field="name"></td>
                <td data-field="house"></td>
                <td data-field="tutor"></td>
                <td data-field="achievement"></td>
                <td data-field="behaviour"></td>
                <td data-field="total"></td>
            </tr>
        </tbody>
    </table>
    
    <script type="text/javascript">
        var returnSuccess = function(params, output) {
                return [
                    200,
                    "success",
                    {
                        "data": output,
                        "status": "success",
                        "params": params
                    }
                ];
            },
            data = [
                { name: 'Student 1', house: 'Bowman', tutor: 'ACR', achievement: 1100, behaviour: 10, total: 1090 },
                { name: 'Student 2', house: 'Fulford', tutor: 'ACR', achievement: 1623, behaviour: 196, total: 1427 }
            ];
    
        $.fixture('GET myapi.get', function(original, settings, headers) {
            return returnSuccess(original.data, data);
        });
    
        $.fixture('GET myapi.sort', function(original, settings, headers) {
            var params = original.data,
                key = params.key,
                direction = params.direction,
                sorted_data = data.sort(function(a, b) {
                    if (direction === 'asc') {
                        if (a[key] < b[key]) {
                            return -1;
                        } else if (a[key] > b[key]) {
                            return 1;
                        } else {
                            return 0;
                        }
                    } else {
                        if (a[key] < b[key]) {
                            return 1;
                        } else if (a[key] > b[key]) {
                            return -1;
                        } else {
                            return 0;
                        }
                    }
                });
    
            return returnSuccess(params, sorted_data);
        });
    
        $.fixture('GET myapi.filter', function(original, settings, headers) {
            var params = original.data,
                filtered_data = [];
    
            for (var key in params) {
                filtered_data = data.filter(function(datum) {
                    if (datum[key].constructor.name !== "String") {
                        return parseInt(params[key], 10) <= datum[key];
                    }
                    return (new RegExp(params[key], 'gi')).test(datum[key]);
                });
            }
            filtered_data = Array.prototype.concat.apply([], filtered_data);
            filtered_data = filtered_data.filter(function(datum, index, self) {
                return self.indexOf(datum) === index;
            });
    
            return returnSuccess(params, filtered_data);
        });
    </script>
    
    <script>
        var promise, renderData, applyTableFilter;
    
        if (steal.then !== undefined) {
            promise = steal('//frogui/components/mysorter/mysorter.js', '//frogui/components/tablefilter/tablefilter.js');
        } else {
            promise = steal.import('frogui/components/mysorter/mysorter!frog-component', 'frogui/components/tablefilter/tablefilter!frog-component');
        }
    
        promise.then(function() {
            var $table = this.element.find('table:first'),
                $tbody = $table.children('tbody'),
                $template = this.element.find('.template tbody > tr'),
                houses = {}, tutor = {};
    
            $table.frogui_components_mysorter({
                headers: {
                    name: 'asc',
                    house: 'asc',
                    tutor: 'asc',
                    achievement: 'asc',
                    behaviour: 'asc',
                    total: 'asc'
                }
            });
    
            applyTableFilter = function() {
                $table.frogui_components_tablefilter({
                    filters: {
                        name: {
                            type: 'text',
                            placeholder: 'Student Name',
                            data: [],
                            position: 0
                        },
                        house: {
                            type: 'select',
                            data: Object.keys(houses),
                            placeholder: 'House',
                            position: 1
                        },
                        tutor: {
                            type: 'select',
                            data: Object.keys(tutor),
                            placeholder: 'Tutor',
                            position: 2
                        },
                        achievement: {
                            type: 'text',
                            placeholder: 'Achievement Points',
                            data: [],
                            position: 3
                        },
                        behaviour: {
                            type: 'text',
                            placeholder: 'Behaviour Points',
                            data: [],
                            position: 4
                        },
                        total: {
                            type: 'text',
                            placeholder: 'Total',
                            data: [],
                            position: 5
                        }
                    }
                });
                $table.find('.filter-row, .filter-submit-row').removeClass('hide');
            };
    
            renderData = function(data) {
                data.forEach(function(datum) {
                    var $row = $template.clone();
    
                    for (var key in datum) {
                        $row.find('[data-field='+key+']').text(datum[key]);
                    }
    
                    houses[datum.house] = null;
                    tutor[datum.tutor] = null;
                    applyTableFilter();
    
                    $tbody.append($row);
                }.bind(this));
            }.bind(this);
    
            $table.on('mysorter.sort', function(ev, sort) {
                Frog.Model.api('myapi.sort', sort)
                    .done(function(resp) {
                        $tbody.empty();
                        renderData(resp.data);
                    });
            });
    
            $table.on('tablefilter.submit', function(ev, data) {
                Frog.Model.api('myapi.filter', data)
                    .done(function(resp) {
                        $tbody.empty();
                        renderData(resp.data);
                    });
            });
    
            $table.on('tablefilter.reset', function(ev, data) {
                Frog.Model.api('myapi.get')
                    .done(function(resp) {
                        $tbody.empty();
                        renderData(resp.data);
                    });
            });
    
            Frog.Model.api('myapi.get')
                .done(function(resp) {
                    renderData(resp.data);
                });
        }.bind(this));
    </script>

    Feel free to tweak how it filters. It's just a rough first pass building on what I posted earlier. This has been a pleasant distraction from the project I'm currently on :)

    Any thoughts @Graham Quince??

    ~ Chris

  4. 34 minutes ago, pconkie said:

    Hi Chris @Chris.Smith

    I've got a couple of questions too....

    1. In this line 

    
    $table = this.element.find('table:first'
    

    what does 'this' refer to? - the document, the html widget, something else?

    2. Is this also the line showing your preferred way to find an element?

    $table = this.element.find('table:first'); - this uses this.element with a tag?

    $template = this.element.find('.template tbody > tr'); this uses this.element with a class?
    How would  $template = $('.template tbody > tr'); be different?

    3. what does .bind(this) do in your script?

    Thanks for your help.

    Paul

    Hi Paul,

    this.element does indeed refer to the HTML widget. this refers to the block scope bound to the executing function (code inside the script tag). element is a property of this.

    this.element.find(selector) is preferred over $(selector) because it is limited to performing a tree search over just the content within that particular HTML widget. This means you won't get any collisions should two instances of this widget be running at the same time. It is also more performant because it is only searching through a handful of DOM elements versus thousands.

    .bind(this) forces a function to operate within a specific block scope. For example:

    Class Person {
    	constructor(name) {
        	this.name = name;
        }
      
        getName() {
        	return this.name;
        }
    }
    
    var paul = new Person("Paul");
    
    // getName returns "Paul"
    assertEquals(paul.getName(), "Paul"); // true
    
    // getName with a custom scope returns "Chris"
    assertEquals(paul.getName.call({ name: 'Chris' }), "Paul"); // false
    assertEquals(paul.getName.call({ name: 'Chris' }), "Chris"); // true
    
    // Binding a function returns a clone of the function
    var bound_function = paul.getName.bind({ name: 'Chris' });
    assertEquals(paul.getName(), "Paul"); // true
    assertEquals(bound_function(), "Chris"); // true
    
    // bound functions can be modified using .call() or .apply()
    assertEquals(bound_function.call(paul), "Chris"); // false
    assertEquals(bound_function.call(paul), "Paul"); // true

    To summarise, .bind() is a static version of .call()  or .apply() however, it does not invoke the function, it merely returns a function that is bound to the provided scope.

    Here is a resource which can probably explain it better than I can - http://devdocs.io/javascript/global_objects/function/bind

    I hope you find this helpful. .bind(this) is fairly advanced stuff and unless you get into writing complex functional code, I think you probably won't use it that much. this.element is important though, and will help you avoid a number of bugs and errors that the community struggled with for a long time before it came along.

    Let me know if you have any more questions,

    ~ Chris

    • Like 1
  5. Hi @mobrien,

    @pconkie's approach is a good start but there are a couple of things that you should both be aware of:

    1. Linking to external libraries in the HTML Widget works differently to how you expect, so the script tag loading in jquery.tablesorter.js from github won't work.
      1. Fortunately, FrogOS already has tablesorter under the hood.
    2. Using ID's is problematic, especially if you want to reuse this code in multiple places.
      1. Combine this.element and use either a tag lookup or add a class
      2. I recommend the using the tag.
    3. Using $(document).ready may produce random results as the document is already ready
      1. Instead, anything that needs to run at the end put in a script tag at the bottom of the code

    Personally, what I would try to do is reduce the amount of data I am storing in HTML by storing my data in JSON. This is a little scary at first but will make modification and maintenance easier.

    <style>
        .template {
            display: none;
        }
    </style>
    
    <table class="table table-striped">
        <thead>
            <th>Name</th>
            <th>House</th>
            <th>Tutor</th>
            <th>Achievement</th>
            <th>Behaviour</th>
            <th>Total</th>
        </thead>
        <tbody></tbody>
    </table>
    
    <table class="template">
        <tbody>
            <tr>
                <td data-field="name"></td>
                <td data-field="house"></td>
                <td data-field="tutor"></td>
                <td data-field="achievement"></td>
                <td data-field="behaviour"></td>
                <td data-field="total"></td>
            </tr>
        </tbody>
    </table>
    
    <script>
        var promise, renderData,
            data = [
                { name: 'Student 1', house: 'Bowman', tutor: 'ACR', achievement: 1100, behaviour: 10, total: 1090 },
                { name: 'Student 2', house: 'Fulford', tutor: 'ACR', achievement: 1623, behaviour: 196, total: 1427 }
            ];
    
        if (steal.then !== undefined) {
            promise = steal('//frogui/components/mysorter/mysorter.js');
        } else {
            promise = steal.import('frogui/components/mysorter/mysorter');
        }
    
        promise.then(function() {
            var $table = this.element.find('table:first'),
                $tbody = $table.children('tbody'),
                $template = this.element.find('.template tbody > tr');
    
            $table.frogui_components_mysorter({
                headers: {
                    name: 'asc',
                    house: 'asc',
                    tutor: 'asc',
                    achievement: 'asc',
                    behaviour: 'asc',
                    total: 'asc'
                }
            });
    
            renderData = function(data) {
                data.forEach(function(datum) {
                    var $row = $template.clone();
    
                    for (var key in datum) {
                        $row.find('[data-field='+key+']').text(datum[key]);
                    }
    
                    $tbody.append($row);
                }.bind(this));
            }.bind(this);
    
            $table.on('mysorter.sort', function(ev, sort) {
                var key = sort.key,
                    direction = sort.direction,
                    sorted_data = data.sort(function(a, b) {
                        if (direction === 'asc') {
                            if (a[key] < b[key]) {
                                return -1;
                            } else if (a[key] > b[key]) {
                                return 1;
                            } else {
                                return 0;
                            }
                        } else {
                            if (a[key] < b[key]) {
                                return 1;
                            } else if (a[key] > b[key]) {
                                return -1;
                            } else {
                                return 0;
                            }
                        }
                    });
    
                $tbody.empty();
                renderData(sorted_data);
            });
    
            renderData(data);
        }.bind(this));
    </script>

    From there you could use the built $.fixture to move the data storage and sorting to an API based pattern. Thats a bit more advanced but would allow filtering of data! Let me know if that is of interest.

     

    ~ Chris

    • Like 2
  6. 2 hours ago, gbligh said:

    Yes please Chris!

     

    5 hours ago, vikpaw said:

    I'm in @Chris.Smith

    Keep your eyes on the FrogCode - Showcase page on the Community as the download links will be going up on there in the next few days.

    ~ Chris

    • Like 1
  7. Hi @pconkie,

    Sorry about that. It appears that your specified theme uuid is incorrect, odd that the information coming back is not entirely useful. Correct your theme uuid and it should work for you.

    You could also try a different method of programmatically creating sites. Unfortunately, we haven't written the documentation for this part of the system yet, so there will be an element of blind programming here.

    // We need the createsite component; if its not loaded, then get it
    // once loaded, run the function passed into .then()
    steal('//frogui/components/createsite/createsite.js').then(function() {
        var $el = $('<div />'); // Create a virtual element to "host" the component.
    
        $el.on('createsite.created', function(ev, site) {
            // A site is born!
            console.log(site.uuid);
        });
      
        $el.on('createsite.error', function() {
            // error handling code goes here
        });
    
        new Frogui.Controllers.Components.Createsite($el, {
            quiet: true,
            title: 'test45785d',
            launchOnCreate: false,
            theme: '7397AE892002C4FABE0FAFC9D06525054B7A007C5EV5C06D',
            description: 'my description',
            pages: [{
                sequence: 0,
                name: 'Page 1',
                layout: 'F7B77DAF2002B37FD67E3F8F9EF9F50BFFB6EE2C088C91F6',
                parent_uuid: null,
                buckets: []
            }]
        });
    });

    This approach is more complex but offers you can easy way of building the pages out. There is a lot of async code happening here, which will require a delicate hand to manage as I imagine you are gonna be looping through loads of data. If you would like more information on this approach let me know.

    Hope this helps :)

    ~ Chris

    • Like 1
  8. Hi @pconkie,

    You are almost there!

    Frog.Model
      .api(
        'sites.create',
        {
          page: [],
          quiet: true,
          name: 'test66',
          description: 'my description',
          theme: '7397AE892002C4FABE0FAFC9D06525054B7A007C5EV5C06D'
        },
        {
          type: 'POST'
        }
      ).done(function(thisResponse) {
    	var site = thisResponse.data.site;
        console.log(site.uuid);
      });

    The third parameter is the options we want to send to jQuery to tell it how to send the request to the server! 

    For more information, check out the api method's documentation here - https://froglearn.backwellschool.net/app/lib/packaging/docs/Com.Frog.Model.html 

    Hope this helps,

     

    Chris

  9. On 29/03/2017 at 10:20, jcraig said:

    Hi

    Unfortunately i cant get to the event you are running so im wondering is there any guides for the frog code widget making process?

     I have been playing with the html widget using javascript to produce lists of students made sites in groups pulled from the MIS and using the username to show external content tailored to the individual user but being able to add it in to a widget is the icing on the cake in my opinion as would mean my users can drop the external content on their own page.

    I would be keen to get something made or even just a quick guide to get started would be grand.

    many thanks

     

    James

     

    Hi James,

    We are about to close down the Closed Beta Program as we transition into the Open Beta phase which would allow you to use FrogCode on your own FrogOS instance! 

    I will flag this to @Graham Quince and we'll see about getting you some information. There is a FrogCode area on the community site (https://frog.frogcommunity.com/frogcode) which has videos and tutorials to prepare you for the event.

    Any questions, Let me know.

     

    ~ Chris

    • Like 1
  10. On 22/03/2017 at 15:18, Graham Quince said:

    OK. made it.

    Welcome to our new FrogCode hub on the community: https://www.frogcommunity.com/frogcode

    In the tutorials section on the hub, you'll find a tutorial showing how to install a downloaded widget, and in the showcase section, you'll find the office365 file to download.

    Ok Folks,

    Public Service Announcement - @ADT found a bug in the Sites integration (in particular the Copy Page functionality). If a FrogCode widget is present on the page, the Copy Page function will break causing the sites application to lock up and hang. At this point the platform is only recoverable by refreshing/closing the tab. I have added a new page to the above site called "Known Bugs" which will have the status and release information for each bug that the community raises. In the meantime, avoid using FrogCode widgets with the Copy Pages functionality; If you need to copy a page with a FrogCode widget on, please temporarily remove the widget, copy the page and replace the widget and manually copy on to the new page.

    • Like 1
  11. On 15/03/2017 at 14:51, Graham Quince said:

    Sorry - there's nowhere for it to live until FrogCode comes online.

     

    On 16/03/2017 at 09:38, Sue Busher said:

    Gutted :(

    @Sue Busher as soon as you get Dickens you will be able to use the Package Manager to import and install widgets and apps found here on the Community! It won't mean that you will be able to tailor the widgets to your own needs but it will give you something in the meantime :).

  12. On 20/03/2017 at 08:52, ADT said:

    Don't like the " will/should" bit!!

    So I need to tell @Graham Quince to stop teasing and start releasing you say...................  im looking more and more forward to Wednesday morning........ :D

    @ADT just checked our nightly builds and can confirm that it is in there. You will need to turn it on in Groups & Policies as it is off by default. There is even fine grained control of the functionality so different staff members can import packages from the community but only admins can install for example... 

    Screen Shot 2017-03-22 at 07.43.21.png

  13. Anyone who is interested, one of the FrogCode Beta testers has written a pretty cool Google Search Widget using FrogCode! They even added two colour picker preferences allowing you to change the colour of the search button!

    Who would be interested in using that widget on their FrogLearn?

    • Like 2
  14. Hi @clangstaff,

    We are experiencing some problems with browser cache management at the moment. Usually, we want the browser to cache as much as possible, with Preview in FrogCode, we need it to forget those files which is harder than it sounds. For the time being try Refreshing the page, or if that doesn't work, clear your browser cache and refresh the page. A fix is in the works presently.

    ~ Chris

×
×
  • Create New...