JS Bind, Apply, Call

Bind

We use the Bind () method primarily to call a function with the this value set explicitly.
It occurs when we use the this keyword when a function or method is invoked.

Bind appear in ECMAScript5, so for older systems you can add this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError ('Function.prototype.bind' +
' - what is trying to be bound is not callable');
}

var aArgs = Array.prototype.slice.call (arguments, 1),
fToBind = this,
fNOP = function () {
},
fBound = function () {
return fToBind.apply (this instanceof fNOP &&
oThis ? this : oThis,
aArgs.concat (Array.prototype.slice.call (arguments)));
};

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP ();

return fBound;
};
}

Let’s use an example: when we click the button we populate the text field randomly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var user = {
data: [
{name: 'T. Woods', age: 37},
{name: 'P. Mickelson', age: 43}
],
clickHandler:function (event) {
// random number between 0 and 1
var randomNum = ((Math.random () * 2 | 0) + 1) - 1;

// This line is adding a random person from the data array to the text field
$ ('input').val (this.data[randomNum].name + " " + this.data[randomNum].age);
}
}

// Assign an eventHandler to the button's click event
$ ('button').click (user.clickHandler);

If we use a framework like JQuery or Backbone.js it will work as it will do most of the binding process for us, but if not this will fail as the method is bound to the HTML element (it is where the method is executed on).

So to bind we change:

1
$ ('button').click (user.clickHandler);

for:

1
$ ('button').click (user.clickHandler.bind (user));

Check also its use for global and local scope:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// This data variable is a global variable
var data = [
{name: 'Sam', age: 12},
{name: 'Alex', age: 14}
];

var user = {
// local data variable
data: [
{name: 'T. Woods', age: 37},
{name: 'P. Mickelson', age: 43}
],
showData: function (event) {
// random number between 0 and 1​
var randomNum = ((Math.random () * 2 | 0) + 1) - 1;
console.log (this.data[randomNum].name + ' ' + this.data[randomNum].age);
}
}

// Assign the showData method of the user object to a variable
var showDataVar = user.showData;
showDataVar (); // Sam 12 (from the global data array, not from the local data array)

// FIX: Bind the showData method to the user object
var showDataVar = user.showData.bind (user);
// Now we get the value from the user object,
// because the 'this' keyword is bound to the user object​
showDataVar (); // P. Mickelson 43

“Apply” and “Call”

Allow us to borrow functions and set the value this in function invocation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// global variable for demonstration
var avgScore = 'global avgScore';

//global function
function avg (arrayOfScores) {
// Add all the scores and return the total
var sumOfScores = arrayOfScores.reduce (function (prev, cur, index, array) {
return prev + cur;
});

// The 'this' keyword here will be bound to the global object,
// unless we set the 'this' with Call or Apply
this.avgScore = sumOfScores / arrayOfScores.length;
}

var gameController = {
scores: [20, 34, 55, 46, 77],
avgScore: null
}

// If we execute the avg function, 'this' inside
// the function is bound to the global window object:
avg (gameController.scores);
// Proof that the avgScore was set on the global window object
console.log (window.avgScore); // 46.4
console.log (gameController.avgScore); // null

// reset the global avgScore
avgScore = 'global avgScore';

// To set the "this" value explicitly, so that 'this' is bound to the gameController,
// We use the call () method:
avg.call (gameController, gameController.scores);

console.log (window.avgScore); //global avgScore
console.log (gameController.avgScore); // 46.4

More info

What is Grunt?

Scripts for frequents tasks such as minify, compile and test.

Install via npm

1
npm install -D grunt

Configuration

via Gruntfile.js file in the actual directory, or the nearest directory if there is none.

  • The console for grunt is Grunt-cli and it can be installed with npm:

    1
    npm install –D -g grunt-cli
  • Parameters for “Grunt-cli”:

    • --gruntfile: specify path for Gruntfile.js
    • --force: forces to continue after errors
    • --tasks: extra dir to scan for finding new taks
    • --npm: scans the new grunt plugins installed with npm to search for new tasks
    • --verbose: show more information about the program execution

Tasks

  • A task is a set of commands which will be executed by grunt: if there is none, we will launch ‘default’.
  • Tasks should be installed via npm before being registered
  • The tasks configurations are set in grunt.initConfig(). Object keys are the tasks names (see line 40)
  • Tasks are loaded in the scheduler following the pattern (see also line 44): grunt.registerTask(taskName, [description, ] tasklist);
  • Multitasks are supported :grunt.registerMultiTask(taskName, [description, ] taskFunction)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name &> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files:{
'dist/<%= pkg.name %>.min.js' : ['<%= <concat.dist.dest %>']
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
//options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
});

grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('mytest', ['jshint']);
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
}
};
  • We can call subtasks of a multitask directly
    1
    2
    grunt concat //executes a multitask
    grunt concat:foo //executes the foo sutask, which is part of concat
  • Tasks can call asynchronous functions.
    1
    2
    3
    4
    5
    grunt.registerTask('asyncme', "My asynchronous task", function(){
    // done will be executed once the task is finished
    var done = this.async();
    doSomethingAsync(done);
    }

Mapping files

  • Parameters:

    • src: source path
    • dest: destination path
    • filter: file system native functions, to filter the files to load (e.g. supports the ** from the relative paths)
    • nonull: if true, it will include files which wont pass the filter
    • dot: add files whose name starts with the chracter ‘.’
    • matchbase: if it’s defined, the files will be searched starting on this dir
    • expand: processes a dynamically the src & dest mapping
  • Different file formats to define a task:

    • Compact
      1
      2
      3
      4
      5
      6
      grunt.initConfig({
      jshint: {
      foo: {
      src: ['src/aa.js', 'src/aaa.js']
      }
      },
    • Object
      1
      2
      3
      4
      5
      6
      7
      8
      grunt.initConfig({
      concat: {
      foo: {
      files: {
      'dest/a.js': ['src/aa.js', 'src/aaa.js'],
      'dest/a1.js': ['src/aa1.js', 'src/aaa1.js']}
      },
      },
    • Array
      1
      2
      3
      4
      5
      6
      7
      8
      grunt.initConfig({
      concat: {
      foo: {
      files: [
      {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
      {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}
      ],
      },
  • Dynamic mapping: we can even combine normal and dynamic mapping: we need to set the expand value to true

    • cwd: ruta del directorio donde se cargan los archivos
    • src: regex to load files
    • dest: destination to save the file
    • ext: reeplaces the file extension of the generated files
    • extDot: points out where is the file extension
    • flatten: deletes all the filepaths from the generated files
    • rename: describes a function which returns a String with the new file src and dest
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      files: [
      {
      expand: true,
      cwd: 'lib/',
      src: ['**/*.js'],
      dest: 'build/',
      ext: '.min.js',
      extDot: 'first'
      },
      ],

Example

Step by step

  1. If we need to load any external JSON property we can get its content into a var via
    1
    grunt.file.readJSON('package.json')
  2. We can call any grunt property directly via
    1
    grunt.config.requires('meta.name')
  3. There are Grunt templates so we can use tags <%= pkg.myvar %>
  4. To load Grunt tasks from npm we can use loadNpmTasks(‘myTaskName’) o loadTasks(‘myTaskName)
  5. Once loaded, we can register the defined task vis grunt.registerTask()
  6. We export the grunt mocule with all the configuration: module.exports = function(grunt) {…}

In a project

  • Gruntfile.js should be at the root folder
  • On node_modules we can see al the Grunt-related modules, which start with grunt-*
  • We can also this dependencies in package.json on devDependences
  • To implement Grunt in any project with the grunt-init module we can create basic templates to start with. Don’t forget to configure its Gruntfile.js

So if we have:

1
2
3
module.exports = function(){
return true;
}

And run grunt myTest with the following Gruntfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = function(grunt){
grunt.initConfig({
uglify: {
src: {
files: [{
expand: true,
cwd: 'src',
src: '*.js',
dest: 'js-min/',
ext: '.min.js'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.reguisterTask('test', ['uglify']);
};

We get as an output js-min/myTest.min.js

1
2
3
module.exports = function(){
return !0;
}

Other examples of defined task

1
2
3
grunt.registerTask('install', [], installTaks);
grunt.registerMultiTask('packgrInstall', 'Download all the assets from dependencies',
function(){installTask(grunt, this); });

Introduction

One of the challenges I had to face a couple of years ago was to migrate a Java desktop application from 32 bits to 64 bits architecture, and what may have been as simple as using the right JDK, switching to the proper external libraries versions and getting rid of the deprecated code actually had an extra issue. The application embeded a 32 bits PDF reader on a panel, and that limitation had been stopping this migration for a while.

One of the challenges I had to face a couple of years ago was to migrate a Java desktop application from 32 bits to 64 bits architecture, and what may have been as simple as using the right JDK, switching to the proper external libraries versions and getting rid of the deprecated code actually had an extra issue. The application embeded a 32 bits PDF reader on a panel, and that limitation had been stopping this migration for a while.

Going into the code itself, it was using the XULRunner, one of the Firefox engine components, so what it actually did was “using a 32 bits Firefox browser embedded”. It also used Adobe Acrobat generated interactive forms, which actually required Adobe Acrobat Reader itself to be rendered properly and this took Apache PDFBox out of the picture to do this task.

When I looked into the philosophy of running an embed browser I tried to take what happened in my laptop: when I was surfing the net and opening a PDF file my Firefox 32 bits browser, I didn’t use some browser code: I delegated this in the Adobe Acrobat Reader X Plugin for the browser. So, why should not we take this idea one step further, and use the same plugin through an integration? Adobe itself provides this system, so there would be no incompatibility issue, I just had to setup this through the operating system configuration to get a workaround and get the proper version for the machine architecture in which we are currently running the program.

So let’s meet DJNativeSwing, a library based in SWT (Standard Widget Toolkit) which allows us to have our very own embedded browser in our code. SWT is highly recommended library due to its portability, as it accesses to the Native Operating System GUIs, and that is exactly what we require for this kind of issue. It is also the next wrapping level to Java Swing, and actually lighter and faster, and I had already used it back in the day to avoid problems when I had to deal with Macromedia Flash plugin integrations.

As an example of how to configure this, we are going to follow 2 basic steps:

  • Set up a browser tab on a Java swing component.
  • Be able to open a PDF on that said browser tab.

Process

1. Previous set up: the Maven dependecies required

Let’s start with the basics: first of all we will get the maven dependencies.

❕This was the stable version when the post was originally written.

1
2
3
4
5
<dependecy>
<group>chrriis.dj.nativeswing</group>
<artifactId>DJNativeSwing</artifactId>
<version>1.0.2</version>
</dependecy>

2. Building a basic Swing interface

Right after that, we will define the basic window frame, with a button the select the file (via FileChooser) and a panel to show the results.

❕Setting up all the text strings as constants makes them easier to spot them in order to replace them if needed. It’s not really necessary, but doing it improves reusability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.Map;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;

import nativeSwing.BrowserPanel;
import pdfHandler.PdfReader;
import xmlHandler.XMLHandler;
import chrriis.common.UIUtils;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;

public class DemoPDFRenderLauncher {

private static final String TITLE = "PDF Renderer demo";
private static final String NO_OUTPUT_MESSAGE = "No output available";
private static final String NO_DATA_MESSAGE = "There is no data available from form";
private static final int LENGTH = 800;
private static final int WIDTH = 600;
private static final String FILTER_FILES "PDF files";
private static final String FILE_EXTENSION "pdf";

/**
* The main app window
*/
private JFrame window;
/**
* The path of the file we will open
*/
private String path;
/**
* Button for open file function
*/
private JButton buttonOpen;
/**
* A browser panel
*/
private BrowserPanel browserPanel;

/**
* Constructor method, creates the GUI
*/
public Launcher() {
window = new JFrame(TITLE);
window.getContentPane().setLayout(new BorderLayout());
window.setSize(LENGTH, WIDTH);
window.add(createButtonsPanel(), BorderLayout.NORTH);
window.add(createContentPanel(), BorderLayout.CENTER);
window.setVisible(true);
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
NativeInterface.close();
System.exit(0);
}
});
}

/**
* Creates a button panel with the action button: open a file
* @return the buttons panel
*/
private Component createButtonsPanel() {
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
buttonOpen = new JButton("Open file");
buttonOpen.addActionListener(new ButtonOpenController());
panel.add(buttonOpen);
return panel;
}

/**
* Creates a panel to render the content
*
* @return the buttons panel
*/
private Component createContentPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JScrollPane scrollPaneText = new JScrollPane(textPanel);
panel.add(scrollPaneText);
//here we will insert the DJNativeSwing panle
browserPanel = new BrowserPanel();
JScrollPane scrollPaneBrowser = new JScrollPane(browserPanel);
panel.add(scrollPaneBrowser);
return panel;
}

/**
* Load button controller, which launches the FileChooser.
*/
private class ButtonOpenController implements ActionListener {
@Override
public void actionPerformed(ActionEvent arg0) {
launchOpenSelectFile();
}
}

/**
* Launches the FileChooser window, and invokes the pdf opener window.
*/
private void launchOpenSelectFile() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setAcceptAllFileFilterUsed(false);
FileNameExtensionFilter filter = new FileNameExtensionFilter(
FILTER_FILES, FILE_EXTENSION);
fileChooser.addChoosableFileFilter(filter);
if (fileChooser.showOpenDialog(window) == JFileChooser.APPROVE_OPTION) {
path = fileChooser.getSelectedFile().getAbsolutePath();
browserPanel.navigate(path);
}
}

@SuppressWarnings("unused")
public static void main(String[] args) {
UIUtils.setPreferredLookAndFeel();
NativeInterface.open();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Launcher demo = new Launcher();
}
});
}
}

3. Embeding a web browser

Then let’s follow up with browser tab: if we just wanted to create a browser panel it would be as easy as writing the following lines:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.awt.BorderLayout;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;

public class BasicBrowserPanel extends JPanel {

private static final String TITLE = "";

/**
* The browser will be handled in this specific component
*/
private JWebBrowser webBrowser;

/**
* Constructor
*/
public BrowserPanel() {
super(new BorderLayout());
JPanel webBrowserPanel = new JPanel(new BorderLayout());
webBrowserPanel.setBorder(BorderFactory.createTitledBorder(TITLE));
webBrowser = new JWebBrowser();
webBrowser.setBarsVisible(false);
webBrowser.setStatusBarVisible(false);
webBrowserPanel.add(webBrowser, BorderLayout.CENTER);
add(webBrowserPanel, BorderLayout.CENTER);
}

/**
* Initializes the browser and sets a value in the URL storage
* @param path the URL value o file path to open
*/
public void navigate(String path) {
webBrowser.setVisible(true);
webBrowser.navigate(path);
}

/**
* Makes the browser retrieve and render the content from the path previously stored
*/
public String getAddress(){
return webBrowser.getHTMLContent();
}

/**
* Hides the browser controls (forward, back, home buttons...)
*/
public void hideContent() {
webBrowser.setVisible(false);
}
}

This way we are getting a fully functional web browser, very similar to the browser in the Eclipse IDE, but with too many unnecessary functions for what we are trying to do here. Since we are only doing the rendering process by delegating it on Adobe, we can remove the GUI extra elements from this whole custom browsing system and leave the bare panel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;

/**
* Allows to launch a JFrame containing an embedded browser.
*/
public class BrowserFrameLauncher {

/**
* Renders the file content on a browser via DJ Native Swing
*
* @param path
* the file url if we pass as a parameter any webpage URL,
* the system would try to render it
* @return a JPanel with a native browser, to render the file content
*/
private Component createBrowserPanel(String path) {
JWebBrowser.useXULRunnerRuntime();
JPanel fileBrowserPanel = new JPanel(new BorderLayout());
final JWebBrowser fileBrowser = new JWebBrowser();
fileBrowser.setBarsVisible(false);
fileBrowser.setStatusBarVisible(false);
fileBrowser.navigate(path);
fileBrowserPanel.add(fileBrowser, BorderLayout.CENTER);
return fileBrowserPanel;
}

}

4. Setting the last piece of the puzzle: getting the PDF itself

Finally let’s make the final touches to open a PDF. What we are actually doing is getting the PDF file path into the browser, so in the end we have a new layer over our old friend XULRunner, but this provides us a way to integrate the plugins via the “right architecture version” SWT library. So as a conclussion we are able to connect to the “right architecture version” plugin, fixing our rendering problem and making us independent from the 32 bits plaform once and for all.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// excerpt from BrowserFrameLauncherPDF.java

private static final String CLOSING_MESSAGE = "Do you really want to close the file?";
private static final String RENDERED_TITLE = "PDF Renderer demo - Embed Browser";

/**
* Opens a file and shows it content in a JFrame.
*
* @param path
* the url of the file to open in a JFrame
*/
public static void openPDF(final String path) {
NativeInterface.open();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final JFrame frame = new JFrame(RENDERED_TITLE);
frame.setLocation(0, 0);
//we may set up a default size for this test
//frame.setSize(800, 600);
frame.setVisible(true);
frame.add(createBrowserPanel(path));

//a window listener would allow us to control the closing actions
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int i = JOptionPane.showConfirmDialog(frame,CLOSING_MESSAGE);
if (i == 0) {
NativeInterface.close();
}
}
});
}
});
}

❗️ Please notice the NativeInterface.open() line to make sure about getting the components correctly booted, and the threading of this component in order to avoid other processes interfering with the rendering.

JQuery

Used for:

  • Manipulate HTML/DOM and CSS.
  • event methods HTML
  • Animations and effects
  • AJAX

Basic syntax:

1
2
3
4
5
6
7
8
9
// don't use Jquery until the page is loaded
$(document).ready(function(){
// jQuery
$(selector).action();
});
$(function(){
// more jQuery after load
$(selector).action();
});
  • $ access to jQuery.
  • (selector) gets HTML element
  • action() does operation on element

Selectors

  • by name, id, class, type, attributes, value…
    1
    2
    3
    4
    5
    6
    7
    $(document).ready(function(){
    // when click on button
    $(' button').click(function(){
    // hide paragraph
    $('p' ).hide();
    });
    });
  • $("#button") -> id="button" -> ids should be unique
  • $(".button") -> class="button" -> may return many

Callbacks on Jquery

  • With callback
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $(document).ready(function(){
    $('button').click(function(){
    $('p').hide('slow', function() {
    // once hidden, show alert
    alert('The paragraph is now hidden');
    });
    });
    });
    //executed first
    alert("The paragraph is not hidden");
  • Without callback
    1
    2
    3
    4
    5
    $('button').click(function(){
    $('p').hide(1000);
    // it will not wait for the paragraphs to be hidden
    alert('The paragraph is now hidden');
    });

Chaining

1
2
3
4
// animation: silde up and down
$('#p1').css('color', 'red')
.slideUp(2000)
.slideDown(2000);

Working with the DOM

No parameters = “gets”. With parameters = “sets”

  • text(): no format, text.
  • html(): with format, HTML
  • val(): value field
  • atr('atrib', 'new value'): set, similar to val('')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(document).ready(function(){
$('#btn1').click(function(){
$('.test1').text(function(i, origText){
return 'Old text: ' + origText
+ ' New text: Hello world! (index: ' + i + ')';
});
});

$('#btn2').click(function(){
$('.test2').html(function(i, origText){
return 'Old html: ' + origText
+ ' New html: Hello <b>world!</b> (index: ' + i + ')';
});
});
});

AJAX

  • allow asynchronous communication bertween server and browser in XML via JS

  • loads data in background and deploys them without a full web reload

  • jQuery uses HTTP GET y POST to obtain text, HTML, XML or JSON de un servidor remoto

  • 3 functions with similar parameters:

    • load

      1
      2
      3
      4
      $(selector).load(url, data, callback);
      // url: mandatory
      // data: <key,value> pairs for the request, optional
      // callback: whet should it do after `load()`, optional
    • get

      1
      $.get(url, callback);
    • post

      1
      $.post(url, data, callback);

Define AMD and async load with RequireJS

Asynchronous Module Definition (AMD)

This API allows to define:

  • modules
  • dependencies

Fast load, improves performance to use/debug.

API specifications

  • Function define()
    Registers a module in the loader:

    1
    define(id?, dependencies?, callback);
  • id: identifier.

    • Optional, usally not included.
    • Should be top level or an absolute id (relative ids are not allowed).
  • dependencies: array of module ids, which are required by the modile we are defining.

    • Its values are solved before executing the callback, and should be apssed via args in the same order as the array.
    • If it is not included, it will use the “require”, “exports” and “module” dependencies.
    • If it is included, the loader should not scan the callback to solve ten dependencies.
  • callback: function to execute to instance the module or an object.

    • If it is a function, it will be executed once.
    • If it is an Object, it should be asigned as value to export from the module.
    • If the callback returns a value, it should be assigned as a value exported from the module.

Property define.amd

  • Avoids conflict with Javascript define()
  • It is not part of the AMD API
  • It can be used for other API implementations (or versions), due to convention

Call more than one module at once

  • Using multiple define calls can be done in a single script.
  • Order should not matter.
  • The loader is responsible of dealing with the dependencies that are not solved until the script is completely loaded to avoid unnecessary requests.
    1
    2
    3
    4
    5
    6
    7
    8
    //anon module which return a literal object
    define(["alpha"], function (alpha) {
    return {
    verb: function(){
    return alpha.verb() + 2;
    }
    };
    });

❗Note: loaders should not add more methods or properties to the define function.

More info on:

RequireJS

RequireJS is the async loader we have been talking about in the AMD section.

  • NodeJs adds a loader which uses the CommonJS formatS, which doesn’t deal well with the browser.
  • Using RequireJS allows us using a single format for all the modules, server or client.
  • It can be configured for only client use.
  • The NodeJs adapter (r.js) for RequireJS will use the require implementation and search for paths in NodeJS
    if the module can’t be found its configuration: RequireJS will assume it is a module which uses config and modules type NodeJS.

Configuration

First we add the library to our project:

1
2
<script data-main="/pages/<%= distName %>/<%= version %>/js/config"
src="/pages/<%= distName %>/<%= version %>/js/vendor/require.js"></script>

By loading RequireJS, we get access to the “require” object. The data-main loads the input point for every dependency on our app (in this case it would be “config.js”, which also configures the “require” object). From here on, we should follow AMD patterns on our modules.
Example:

1
2
3
require(["jquery"], function($) {
$("#mydiv").html("Hello this is RequireJS talking");
});

More info on:

0%