Can You Pass Jinja Templating To Jquery Script
Customizing CKAN'southward JavaScript¶
JavaScript code in CKAN is broken downwardly into modules: pocket-size, contained units of JavaScript code. CKAN themes can add JavaScript features by providing their own modules. This tutorial will explain the main concepts involved in CKAN JavaScript modules and walk y'all through the process of adding custom modules to themes.
Overview¶
The idea backside CKAN'south JavaScript modules is to keep the code uncomplicated and easy to test, debug and maintain, by breaking information technology down into pocket-sized, independent modules. JavaScript modules in CKAN don't share global variables, and don't telephone call each other's code.
These JavaScript modules are attached to HTML elements in the page, and enhance the functionality of those elements. The idea is that an HTML chemical element with a JavaScript module fastened should yet be fully functional even if JavaScript is completely disabled (e.yard. considering the user'southward web browser doesn't support JavaScript). The user feel may not be quite as squeamish without JavaScript, but the functionality should still be there. This is a programming technique known as graceful degradation, and is a bones tenet of web accessibility.
In the sections below, nosotros'll walk yous through the steps to add a new JavaScript feature to CKAN - dataset info popovers. We'll add an info button to each dataset on the datasets page which, when clicked, opens a popover containing some extra information and user deportment related to the dataset:
Initializing a JavaScript module¶
To get CKAN to phone call some custom JavaScript code, we need to:
-
Implement a JavaScript module, and register it with CKAN. Create the file
ckanext-example_theme/ckanext/example_theme_docs/assets/example_theme_popover.js, with these contents:// Enable JavaScript's strict mode. Strict mode catches some common // programming errors and throws exceptions, prevents some dangerous actions from // being taken, and disables some disruptive and bad JavaScript features. "apply strict" ; ckan . module ( 'example_theme_popover' , function ( $ ) { render { initialize : office () { console . log ( "I've been initialized for element: " , this . el ); } }; });
This bit of JavaScript calls the
ckan.module()function to register a new JavaScript module with CKAN.ckan.module()takes two arguments: the proper name of the module being registered ('example_theme_popover'in this example) and a function that returns the module itself. The function takes two arguments, which we'll expect at afterwards. The module is just a JavaScript object with a single attribute,initialize, whose value is a role that CKAN will call to initialize the module. In this case, the initialize function just prints out a confirmation message - this JavaScript module doesn't do annihilation interesting still.Note
JavaScript module names should begin with the proper noun of the extension, to avoid conflicting with other modules. See Avert name clashes.
Annotation
Each JavaScript module's
initialize()function is chosen on DOM ready. -
Include the JavaScript module in a page, using Assets, and utilise information technology to ane or more HTML elements on that folio. Nosotros'll override CKAN's
package_item.htmltemplate snippet to insert our module whenever a package is rendered as part of a list of packages (for example, on the dataset search page). Create the fileckanext-example_theme/ckanext/example_theme_docs/templates/snippets/package_item.htmlwith these contents:{% ckan_extends %} {% block content %} {{ super () }} {# Apply Webassets to include our custom JavaScript module. A <script> tag for the module will be inserted in the right identify at the bottom of the page. #} {% nugget 'example_theme/example_theme' %} {# Apply our JavaScript module to an HTML element. The data-module attribute, which tin can be practical to any HTML element, tells CKAN to initialize an instance of the named JavaScript module for the element. The initialize() method of our module will be called with this HTML element every bit its this.el object. #} <push button data-module="example_theme_popover" class="btn" href="#"> <i class="fa fa-info-circle"></i> </push> {% endblock %}
If you now restart the development server and open http://127.0.0.1:5000/dataset in your web browser, you should see an extra info button next to each dataset shown. If you open up a JavaScript console in your browser, you should see the message that your module has printed out.
See besides
Near web browsers come up with built-in programmer tools including a JavaScript console that lets you see text printed by JavaScript code to
console.log(), a JavaScript debugger, and more. For example:-
Firefox Developer Tools
-
Firebug
-
Chrome DevTools
If y'all have more than one dataset on your page, you'll see the module'south message printed one time for each dataset. The
package_item.htmltemplate snippet is rendered in one case for each dataset that's shown in the listing, then your<push>element with thedata-module="example_theme_popover"attribute is rendered once for each dataset, and CKAN creates a new example of your JavaScript module for each of these<button>elements. If you view the source of your folio, even so, you'll run into thatexample_theme_popover.jsis merely included with a<script>tag once. Assets is smart enough to deduplicate resources.Notation
JavaScript modules must be included as Assets resources, yous can't add them to a
publicdirectory and include them using your own<script>tags. -
this.options and this.el ¶
Now let's start to brand our JavaScript module exercise something useful: show a Bootstrap popover with some extra info almost the dataset when the user clicks on the info button.
Get-go, we demand our Jinja template to pass some of the dataset'due south fields to our JavaScript module equally options. Change package_item.html to look like this:
{% ckan_extends %} {% cake content %} {{ super () }} {% asset 'example_theme/example_theme' %} {# Employ our JavaScript module to an HTML <button> element. The additional data-module-* attributes are options that volition be passed to the JavaScript module. #} <push button data-module="example_theme_popover" data-module-title=" {{ package.championship }} " data-module-license=" {{ package.license_title }} " information-module-num_resources=" {{ package.num_resources }} "> <i class="fa fa-info-circle"></i> </button> {% endblock %} This adds some data-module-* attributes to our <push> element, east.g. information-module-title="{{ package.title }}" ( {{ bundle.championship }} is a Jinja2 expression that evaluates to the title of the dataset, CKAN passes the Jinja2 variable package to our template).
Alarm
Although HTML five treats any attribute named data-* every bit a data attribute, merely attributes named data-module-* will be passed equally options to a CKAN JavaScript module. So nosotros have to named our parameters data-module-title etc., non just data-title .
Now let's make use of these options in our JavaScript module. Modify example_theme_popover.js to look like this:
"employ strict" ; /* example_theme_popover * * This JavaScript module adds a Bootstrap popover with some actress info about a * dataset to the HTML element that the module is practical to. Users tin click * on the HTML chemical element to show the popover. * * title - the title of the dataset * license - the championship of the dataset's copyright license * num_resources - the number of resources that the dataset has. * */ ckan . module ( 'example_theme_popover' , function ( $ ) { render { initialize : office () { // Access some options passed to this JavaScript module by the calling // template. var num_resources = this . options . num_resources ; var license = this . options . license ; // Format a simple string with the number of resources and the license, // e.m. "3 resources, Open Data Commons Attribution License". var content = 'NUM resources, LICENSE' . replace ( 'NUM' , this . options . num_resources ) . replace ( 'LICENSE' , this . options . license ) // Add a Bootstrap popover to the HTML element (this.el) that this // JavaScript module was initialized on. this . el . popover ({ championship : this . options . title , content : content , placement : 'left' }); } }; }); Note
Information technology's best practice to add a docstring to the superlative of a JavaScript module, as in the instance above, briefly documenting what the module does and what options it takes. See JavaScript modules should have docstrings.
Any data-module-* attributes on the HTML chemical element are passed into the JavaScript module in the object this.options :
var num_resources = this . options . num_resources ; var license = this . options . license ; A JavaScript module tin can access the HTML element that it was applied to through the this.el variable. To add together a popover to our info push, nosotros phone call Bootstap'south popover() function on the element, passing in an options object with some of the options that Bootstrap's popovers accept:
// Add a Bootstrap popover to the HTML chemical element ( this . el ) that this // JavaScript module was initialized on . this . el . popover ({ title : this . options . title , content : content , placement : 'left' }); Default values for options¶
Default values for JavaScript module options can be provided by calculation an options object to the module. If the HTML chemical element doesn't take a information-module-* aspect for an option, then the default will be used instead. For instance…
Todo
Remember of an example to practise using default values.
Ajax, event handling and CKAN's JavaScript sandbox¶
Then far, nosotros've used simple JavaScript string formatting to put together the contents of our popover. If we want the popover to contain much more circuitous HTML nosotros actually need to return a template for it, using the full power of Jinja2 templates and CKAN'southward template helper functions. Allow's edit our plugin to employ a Jinja2 template to render the contents of the popups nicely.
Get-go, edit package_item.html to make it laissez passer a few more parameters to the JavaScript module using data-module-* attributes:
{% ckan_extends %} {% cake content %} {{ super () }} {% asset 'example_theme/example_theme' %} <button data-module="example_theme_popover" information-module-id=" {{ packet.id }} " data-module-championship=" {{ package.title }} " data-module-license_title=" {{ parcel.license_title }} " data-module-num_resources=" {{ package.num_resources }} "> <i class="fa fa-info-circumvolve"></i> </push> {% endblock %} We've also added a second {% asset %} tag to the snippet above, to include a custom CSS file. We'll see the contents of that CSS file afterwards.
Side by side, nosotros need to add a new template snippet to our extension that will be used to render the contents of the popovers. Create this example_theme_popover.html file:
ckanext - example_theme / ckanext / example_theme / templates / ajax_snippets / example_theme_popover . html and put these contents in it:
{# The contents for a dataset popover. id - the id of the dataset num_resources - the dataset's number of resources license_title - the dataset'due south license championship #} <div course="context-info"> <div course="nums"> <dl> <dt> {{ _ ( 'Followers' ) }} </dt> <dd> {{ h.follow_count ( 'dataset' , id ) }} </dd> <dt> {{ _ ( 'Resource' ) }} </dt> <dd> {{ num_resources }} </dd> </dl> </div> <div class="license"> <dl> <dt>License</dt> <dd> {{ license_title }} </dd> </dl> </div> <div grade="clearfix"></div> {{ h.follow_button ( 'dataset' , id ) }} <a grade="btn go-to-dataset" href=" {{ h.url_for ( 'dataset.read' , id = id ) }} "> <i class="fa fa-pointer-circle-right"></i> Become to dataset </a> </div> This is a Jinja2 template that renders some nice looking contents for a popover, containing a few bits of information about a dataset. It uses a number of CKAN's Jinja2 templating features, including marking user-visible strings for translation and calling template helper functions. Encounter Customizing CKAN'south templates for details nearly Jinja2 templating in CKAN.
Note
The new template file has to exist in a templates/ajax_snippets/ directory and so that we tin use the template from our JavaScript code using CKAN'southward getTemplate() function. Only templates from ajax_snippets directories are available from the getTemplate() function.
Next, edit assets/example_theme_popover.js every bit shown below. There's a lot going on in this new JavaScript code, including:
-
Using Bootstrap's popover API to show and hibernate popovers, and gear up their contents.
-
Using jQuery'south event handling API to go our functions to be chosen when the user clicks on a push.
-
Using a office from CKAN's JavaScript sandbox.
The sandbox is a JavaScript object, available to all JavaScript modules as
this.sandbox, that contains a drove of useful functions and variables.this.sandbox.clientis a CKAN API client written in JavaScript, that should exist used whenever a JavaScript module needs to talk to the CKAN API, instead of modules doing their ain HTTP requests.this.sandbox.customer.getTemplate()is a part that sends an asynchronous (ajax) HTTP request (i.east. send an HTTP request from JavaScript and receive the response in JavaScript, without causing the browser to reload the whole page) to CKAN asking for a template snippet to be rendered.
Hopefully the liberal commenting in the code below makes it clear plenty what'south going on:
"use strict" ; ckan . module ( 'example_theme_popover' , office ( $ ) { return { initialize : part () { // proxyAll() ensures that whenever an _on*() function from this // JavaScript module is called, the `this` variable in the role will // be this JavaScript module object. // // Y'all probably desire to call proxyAll() like this in the initialize() // function of about modules. // // This is a shortcut role provided by CKAN, information technology wraps jQuery'southward // proxy() role: http://api.jquery.com/jQuery.proxy/ $ . proxyAll ( this , /_on/ ); // Add a Bootstrap popover to the button. Since nosotros don't have the HTML // from the snippet yet, nosotros merely set the content to "Loading..." this . el . popover ({ title : this . options . title , html : true , content : this . _ ( 'Loading...' ), placement : 'left' }); // Add an consequence handler to the button, when the user clicks the button // our _onClick() function will be called. this . el . on ( 'click' , this . _onClick ); }, // Whether or non the rendered snippet has already been received from CKAN. _snippetReceived : false , _onClick : function ( upshot ) { // Send an ajax request to CKAN to render the popover.html snippet. // We wrap this in an if statement considering we only want to asking // the snippet from CKAN in one case, not every time the button is clicked. if ( ! this . _snippetReceived ) { this . sandbox . client . getTemplate ( 'example_theme_popover.html' , this . options , this . _onReceiveSnippet ); this . _snippetReceived = true ; } }, // CKAN calls this function when it has rendered the snippet, and passes // it the rendered HTML. _onReceiveSnippet : function ( html ) { // Replace the popover with a new one that has the rendered HTML from the // snippet every bit its contents. this . el . popover ( 'destroy' ); this . el . popover ({ championship : this . options . title , html : true , content : html , placement : 'left' }); this . el . popover ( 'bear witness' ); }, }; }); Finally, we need some custom CSS to make the HTML from our new snippet look nice. In package_item.html to a higher place we added a {% asset %} tag to include a custom CSS file. Now we demand to create that file, ckanext-example_theme/ckanext/example_theme/avails/example_theme_popover.css :
. dataset-listing . popover . nums { /* We're reusing the .nums class from the dataset read folio, * but we don't want the border, margin and padding, go rid of them. */ edge : none ; margin : 0 ; padding : 0 ; /* We want the license and numbers to appear side by side, so float the * numbers list to the left and go far take upwards just over half of * the width of the popover. */ bladder : left ; width : 55 % ; } . dataset-listing . popover . license { /* Forestall the words in the license from being wrapped mid-word. */ word-pause : keep-all ; } . dataset-list . popover . go-to-dataset { /* Float the "Go to dataset" button to the right side of the popover, * this puts some space between the two buttons. */ float : right ; } Restart CKAN, and your dataset popovers should be looking much ameliorate.
Error handling¶
What if our JavaScript makes an Ajax request to CKAN, such as our getTemplate() phone call above, and gets an error in response? We can simulate this by changing the name of the requested template file to one that doesn't exist:
this . sandbox . client . getTemplate ( 'foobar.html' , this . options , this . _onReceiveSnippet ); If you reload the datasets page after making this change, you'll see that when you click on a popover its contents remain Loading…. If you have a development console open in your browser, you lot'll see the fault response from CKAN each fourth dimension yous click to open a popover.
Our JavaScript module's _onReceiveSnippet() function is merely called if the request gets a successful response from CKAN. getTemplate() also accepts a second callback function parameter that volition exist called when CKAN sends an error response. Add this parameter to the getTemplate() call:
this . sandbox . client . getTemplate ( 'foobar.html' , this . options , this . _onReceiveSnippet , this . _onReceiveSnippetError ); } }, Now add the new error function to the JavaScript module:
_onReceiveSnippetError : function ( mistake ) { this . el . popover ( 'destroy' ); var content = error . status + ' ' + mistake . statusText + ' :(' ; this . el . popover ({ title : this . options . championship , html : truthful , content : content , placement : 'left' }); this . el . popover ( 'show' ); this . _snippetReceived = true ; }, After making these changes, you should see that if CKAN responds with an error, the contents of the popover are replaced with the error bulletin from CKAN.
Pubsub¶
You lot may have noticed that, with our example code so far, if y'all click on the info button of one dataset on the folio then click on the info button of another dataset, both dataset'due south popovers are shown. The get-go popover doesn't disappear when the second appears, and the popovers may overlap. If y'all click on all the info buttons on the folio, popovers for all of them will be shown at once:
To brand i popover disappear when another appears, we tin utilise CKAN's publish() and subscribe() functions. These pair of functions let different instances of a JavaScript module (or instances of different JavaScript modules) on the same page to talk to each other. The mode it works is:
-
Modules can subscribe to events by calling
this.sandbox.client.subscribe(), passing the 'topic' (a string that identifies the type of event to subscribe to) and a callback function. -
Modules can call
this.sandbox.client.publish()to publish an event for all subscribed modules to receive, passing the topic string and one or more further parameters that will be passed on as parameters to the receiver functions. -
When a module calls
publish(), whatever callback functions registered by previous calls tosubscribe()with the same topic string will be called, and passed the parameters that were passed to publish. -
If a module no longer wants to receive events for a topic, information technology calls
unsubscribe().All modules that subscribe to events should have a
teardown()role that unsubscribes from the event, to foreclose memory leaks. CKAN calls theteardown()functions of modules when those modules are removed from the folio. Come across JavaScript modules should unsubscribe from events in teardown().
Remember that because we adhere our example_theme_popover.js module to a <button> element that is rendered once for each dataset on the page, CKAN creates one instance of our module for each dataset. The only fashion these objects can communicate with each other so that i object can hibernate its popover when some other object shows its popover, is by using pubsub.
Hither's a modified version of our example_theme_popover.js file that uses pubsub to brand the dataset popovers disappear whenever a new popover appears:
"apply strict" ; ckan . module ( 'example_theme_popover' , function ( $ ) { return { initialize : part () { $ . proxyAll ( this , /_on/ ); this . el . popover ({ championship : this . options . championship , html : true , content : this . _ ( 'Loading...' ), placement : 'left' }); this . el . on ( 'click' , this . _onClick ); // Subscribe to 'dataset_popover_clicked' events. // Whenever any line of code publishes an consequence with this topic, // our _onPopoverClicked function volition be called. this . sandbox . subscribe ( 'dataset_popover_clicked' , this . _onPopoverClicked ); }, teardown : function () { this . sandbox . unsubscribe ( 'dataset_popover_clicked' , this . _onPopoverClicked ); }, _snippetReceived : false , _onClick : function ( event ) { if ( ! this . _snippetReceived ) { this . sandbox . client . getTemplate ( 'example_theme_popover.html' , this . options , this . _onReceiveSnippet ); this . _snippetReceived = true ; } // Publish a 'dataset_popover_clicked' event for other interested // JavaScript modules to receive. Laissez passer the push that was clicked as a // parameter to the receiver functions. this . sandbox . publish ( 'dataset_popover_clicked' , this . el ); }, // This callback function is called whenever a 'dataset_popover_clicked' // event is published. _onPopoverClicked : function ( push button ) { // Wrap this in an if, considering we don't want this object to reply to // its own 'dataset_popover_clicked' result. if ( push button != this . el ) { // Hide this button'south popover. // (If the popover is not currently shown anyway, this does aught). this . el . popover ( 'hide' ); } }, _onReceiveSnippet : function ( html ) { this . el . popover ( 'destroy' ); this . el . popover ({ title : this . options . title , html : truthful , content : html , placement : 'left' }); this . el . popover ( 'show' ); }, }; }); jQuery plugins¶
CKAN provides a number of custom jQuery plugins for JavaScript modules to use by default, meet CKAN jQuery plugins reference. Extensions tin can also add their own jQuery plugins, and the plugins will and then be available to all JavaScript code via the this.$ object.
See also
- How to Create a Basic Plugin
-
jQuery's ain documentation on writing jQuery plugins. Read this for all the details on writing jQuery plugins, here we'll only provide a simple example and show you lot how to integrate it with CKAN.
It's a good idea to implement any JavaScript functionality not directly related to CKAN equally a jQuery plugin. That way your CKAN JavaScript modules will be smaller as they'll contain only the CKAN-specific code, and your jQuery plugins will besides be reusable on non-CKAN sites. CKAN core uses jQuery plugins to implement features including date formatting, warning users about unsaved changes when leaving a folio containing a form without submitting the form, restricting the set of characters that can exist typed into an input field, etc.
Let'south add a jQuery plugin to our CKAN extension that makes our info buttons turn green when clicked.
Todo
Replace this with a more than realistic case.
Outset nosotros need to write the jQuery plugin itself. Create the file ckanext-example_theme/ckanext/example_theme/assets/jquery.greenify.js with the following contents:
"utilise strict" ; ( function ( jQuery ) { jQuery . fn . greenify = function () { this . css ( "colour" , "greenish" ); return this ; }; })( this . jQuery ); If this JavaScript code looks a little disruptive at starting time, it'south probably because it's using the Immediately-Invoked Function Expression (IIFE) blueprint. This is a common JavaScript code design in which an anonymous part is created and then immediately chosen once, in a single expression. In the instance to a higher place, we create an unnamed function that takes a single parameter, jQuery , so we call the role passing this.jQuery to its jQuery parameter. The lawmaking inside the trunk of the part is the important part. Writing jQuery plugins in this style ensures that any variables divers inside the plugin are private to the plugin, and don't pollute the global namespace.
In the body of our jQuery plugin, we add together a new function called greenify() to the jQuery object:
jQuery . fn . greenify = function () { this . css ( "colour" , "green" ); return this ; }; jquery.fn is the jQuery prototype object, the object that normal jQuery objects become all their methods from. Past adding a method to this object, we enable any lawmaking that has a jQuery object to telephone call our method on any HTML element or set of elements. For example, to turn all <a> elements on the folio greenish you could exercise: jQuery('a').greenify() .
The code inside the greenify() office just calls jQuery'southward standard css() method to fix the CSS color attribute of the element to green . This is just standard jQuery code, except that within a custom jQuery function yous use this to refer to the jQuery object, instead of using $ or jquery (equally you lot would unremarkably exercise when calling jQuery methods from code external to jQuery).
Our method then returns this to permit jQuery method chaining to exist used with our method. For example, a user tin set an element's CSS color aspect to greenish and add together the CSS class greenified to the element in a unmarried expression by chaining our jQuery method with another method: $('a').greenify().addClass('greenified');
Earlier we tin can use our greenify() method in CKAN, we demand to import the jquery.greenify.js file into the CKAN page. To do this, add together a {% asset %} tag to a template file, just as you would do to include any other JavaScript or CSS file in CKAN. Edit the package_item.html file:
{% ckan_extends %} {% block content %} {{ super () }} {% nugget 'example_theme/example_theme' %} <push button information-module="example_theme_popover" data-module-id=" {{ package.id }} " data-module-title=" {{ bundle.title }} " data-module-license_title=" {{ package.license_title }} " information-module-num_resources=" {{ package.num_resources }} "> <i course="fa fa-info-circle"></i> </button> {% endblock %} Now we can call the greenify() method from our example_theme_popover JavaScript module. For instance, we could add a line to the _onClick() method in example_theme_popover.js then that when a dataset info button is clicked it turns green:
_onClick : office ( issue ) { // Make all the links on the page plough green. this . $ ( 'i' ). greenify (); if ( ! this . _snippetReceived ) { this . sandbox . client . getTemplate ( 'example_theme_popover.html' , this . options , this . _onReceiveSnippet ); this . _snippetReceived = true ; } this . sandbox . publish ( 'dataset_popover_clicked' , this . el ); }, Internationalization¶
See Internationalizing strings in JavaScript lawmaking.
Testing JavaScript modules¶
Todo
Evidence how to write tests for the instance module.
Can You Pass Jinja Templating To Jquery Script,
Source: https://docs.ckan.org/en/latest/theming/javascript.html
Posted by: mackwently99.blogspot.com

0 Response to "Can You Pass Jinja Templating To Jquery Script"
Post a Comment