Liferay.com, mobile sites and responsive layouts

May 22, 2011 By Nate Cavanaugh

Bryan recently blogged about the new site design and how Liferay.com is displaying a mobile formatted website for different types of devices (go ahead and test it out, hop into any browser and resize the page as small as it can go). Today I want to discuss a bit how we achieved this from a technical level. This is one of the most common questions I not only got from people who attended our East Coast Symposium, but also inside of Liferay from other devs.

Liferay.com can resize from desktop to mobile dynamically

Because we like to keep our ear to the ground on all things front end, we’ve actually been following the discussion/debate about responsive web design since Ethan Marcotte published his article on the subject last year. If you haven’t read the article, the tl;dr version is that using CSS media queries, it’s possible to pass a different set of CSS styles to different devices and have a design that’s customized for a specific browser/device size.

As far as mobile strategies go, it’s probably the easiest to implement, and there are a lot of quick wins with it. However, Jon Neal pointed out to me when we were first discussing this idea that there’s very little difference between using CSS media queries and just using Javascript to simulate the same thing.

I’ve since read other people bring up a similar point, and it’s a fair one. In fact, there are multiple issues with CSS media queries that make them cumbersome. For instance, let’s say we want to share some styling across two specific devices. It’s possible using the logical operators but even then, you end up with duplicate CSS (and it becomes much harder to read). And the fact that IE doesn’t support media queries was another major impediment to us using it, so we were going to need to use JavaScript anyways (either to add the CSS classnames or to parse mediaqueries).

So Jon wrote up a script that would handle this logic for us. Here’s how the script works: When the page is loaded or resized, the script checks the width of the window and it looks into an object of 4 major size specifications; 960, 720, 480, and 320.

What do those numbers mean? 960px is based on the uber-popular 960 grids which is a layout width that looks great on most desktop computers, and the iPad in landscape mode. 720px is a layout width that looks great when the iPad is in portrait mode. 480px is the width of the window of most smart phones in landscape mode. 320px is the width of the window of most smart phones in portrait mode.

The other great thing Jon came up with was to add greater-than and less-than css classes. I’ll show how this works in a second, but the idea is basically, you can not only target a specific device width, but also if it’s greater than or less than any of those above widths.

If your target device is 600x800, you can still target that device with the css classes.

With what Jon prototyped, I’ve created an Alloy module that codifies this so that it’s super easy to use (while at the same time, staying out of the way for users who don’t wish to use it).

For a quick and simple demo, open up the viewport demo (if you view it in Chrome or Safari, Firefox or Opera, you’ll see some cool CSS3 generated content/animation, but the demo works in any browser).

To get the module to run, all you need to do is call: AUI().use('aui-viewport') in a script tag anywhere on your page.

Now you can target your site design to specific device sizes. Let’s say we want to give our navigation items to sit side by side when we view on the ipad or larger, but that we want to have them stack on smart phones:

#navigation li {
    display: inline;
    float: left;
}
.aui-view-lt720 #navigation li {
    display: block;
    float: none;
}

or let’s say we don’t want to make sure our images don’t exceed a certain width on the iPad in portrait mode

.aui-view-720 img {
    max-width: 300px
}

Or perhaps we want to target just portrait mode of smartphones and tablets:

.aui-view-720 body, .aui-view-320 body {
    background: #ccc;
}

You can even combine this with our other browser selectors to target very specific browsers:

.touch.aui-view-lt720 {} /* Touch based smartphones */
.webkit.aui-view-lt960 {} /* Webkit based tablets and smartphones */
.win.aui-view-720 {} /* Smaller browser views on just Windows */

Now, how is the logic applied? If the screen is equal to or between any of our defined widths, it will get this CSS class: aui-view-{WIDTH}. And, if the screen is greater than any of our widths, it will also get: aui-view-gt{WIDTH}. Lastly, if the screen is less than any of our widths, it will also get: aui-view-lt{WIDTH}.

So a window size of 1900x1200 would get:

aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-gt960
aui-view-960

whereas a window size of 800x600 would get:

aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-720
aui-view-lt960

Notice that the classes represent the width rounded down to the nearest division. This is to guarantee that your styled layout won’t exceed the current width. Because of that the css class doesn’t exactly represent the current width, but rather enforces you to think about the layout at specified sizes and devices.

Caveats

Life is not all cheese and bananas with this system, as there are some caveats to be aware of:

  • You are delivering the same content to all devices.

    Depending on the project, this could either seal the deal or break it. There’s an added cost to shipping down different images for the same page, and some mobile websites may wish to send only a very limited set of content to mobile devices (or perhaps very targeted content). The answer to this then is decide which of the 2 strategies you wish to use for your specific project: same content for all devices, but dimension specific designs, or redirect to a mobile/touch specific community that serves a targeted set of content and design.

  • Designs can require more planning for each of the design layouts

    You’ll notice that the design now has to accommodate basically 4 different views: desktop, tablet(portrait), and smartphone (portrait and landscape). This of course means thinking about a different set of design compromises and layouts for how the same content should fit (or which content should be hidden) for different types of views. Of course, this caveat extends to both content strategies, but having the same content automatically in every view often requires a bit more forethought during the design process.

How can you get this module?

If you’re an Enterprise Edition customer, this module will be available in the 6.0 SP2 service pack (and the great part is, if you don’t want to use it, it will have 0 affect on your application). If you’re running Community Edition, this will be in Liferay 6.1 or if you’re running trunk of Liferay you can see it today. We’ll also be releasing another version of AlloyUI soon that will have it included.

Conclusions

Overall, the response has been great, and the design/development process was surprisingly smooth. I would recommend it as a general principle for your design, as it helps our designs fit the fluid nature of the web.

More information

Responsive Web Design by Ethan Marcotte
MediaQueri.es
The practicalities of CSS Media Queries, lessons learned.

Using jQuery (or any Javascript library) in Liferay 6.0

July 20, 2010 By Nate Cavanaugh

 One of the biggest feature requests from Liferay 5.2 was the ability to upgrade the included version of jQuery. Many users would like to use third-party plugins, and most of those require the latest jQuery library (1.4.x as of this writing).

So for 6.0, we solved this a couple of different ways. First, we no longer include jQuery by default. We have rebuilt our UI to run off of AlloyUI which is built on top of YUI3.
By moving off of jQuery, it's also allowed us to step out of the way of developers who wish to  use any version of jQuery that they need without worrying about conflicts with the core portal javascript.
The other way we solved this for the future was by creating our own namespace. Since we're still using a Javascript library (YUI3), we would still have the same risk of conflicts.
So instead of calling YUI() in the portal, we created AUI(). By creating the "AUI" namespace, we are able to guarantee that our environment won't conflict with someone who wants to upgrade their version of YUI3 in the future.
 
But even though we believe strongly in AlloyUI and YUI3, there are existing applications with codebases on jQuery and porting them over is not always possible.
Or perhaps there is some other Javascript library (such as YUI2, Dojo, qooxdoo, ExtJS, etc) that you need to include for the same reason.
 
So today, I want to show a couple of ways to include the third-party javascript library into Liferay that you want. I'll be using jQuery, and I'll be using the URL to their "production" version: http://code.jquery.com/jquery-1.4.2.min.js
 
There are a couple of ways you can include jQuery onto the page.
Jonas has covered a great way in his blog post on building jQuery portlets in Liferay 6.
 
First, using the same basic principle, is including it in your portlet.
In your liferay-portlet.xml add this entry:
<header-portlet-javascript>http://code.jquery.com/jquery-1.4.2.min.js</header-portlet-javascript>
That will add jQuery 1.4.2 onto the page wherever that portlet happens to be rendered.
 
Second, the other way is to add it into your theme. Inside of your theme's templates/portal_normal.vm you would add this line in the head of your theme:
<script src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
This will make jQuery available for everywhere in Liferay, including plugins that you deploy.
 
Third, you can even use AlloyUI to load up other JS files. This is useful if you can't or don't want to edit either the liferay-portlet.xml or the HTML.
 
In any Javascript that gets added to the page, you can do:
AUI().use('get', function(A){
    A.Get.script('http://code.jquery.com/jquery-1.4.2.min.js', {

       onSuccess: function(){
           // jQuery() can be used in here...

      }
 });
});
 
Also, I want to point out that for those people who built apps on Liferay with the version of jQuery that was included (1.2.6), and if you would like to continue to use that version and the plugins that were included by default, we have kept that directory in the source for 6.0 as a deprecated fallback.
 
This allows people who don't want to upgrade the JS portion of their app, they can easily include the previous version.
The way that would look different is that it would just point to the different path, like so:
<header-portlet-javascript>/html/js/jquery/jquery.js</header-portlet-javascript>
The path to the previous version is:
/html/js/jquery/jquery.js and the /html/js/jquery/ directory contains all of the plugins from 5.2 that work with jQuery 1.2.6.
 
I hope that is helpful, and much thanks to Jonas for his blog post about the sample jQuery plugin. And of course, please let me know if there are any questions :)

AlloyUI - Working with Widgets, Part 1

April 14, 2010 By Nate Cavanaugh

 What is an Alloy widget?

A widget is something that does some sort of task. Like what, you might ask? For instance, in Alloy, there's a TreeView widget, where you can configure it to display an hierarchy of data, and this hierarchy can be ajaxed in, or shown right away, and with a lot of different options.
That would be a pretty complex widget.
A simple widget would be the Tooltip widget, which just shows users some content when they hover over an item on the page.
 
In Alloy, there's a large collection of widgets, as well as a robust framework for building your own.
 
In this blog post, I'm going to cover some of the widgets that we have in Alloy, then in the next one, I'll post how to create your own. I'm splitting it up because I'm finding both posts have a lot of info to cover, and I don't want people to zone out or miss info they might have found useful because it was buried in a super long post.
 
So let's go ahead and get our sandbox ready:
 
AUI().use('', function(A){
 // Our code will go here
});

I'll reference the name of the widget above the code so you can see the module that we'll be using in order to run it.
 
So let's go ahead and create a TreeView of a user directory that has drag and dropping of elements, and expandable folders.
 
Here's the demo: TreeWidget
Here's the code you would have to write:
 
You would use the aui-tree-view module
 
var users = new A.TreeViewDD({
     children: [{
           label: 'Users',
           expanded: true,
           children: [{
                label: 'Nate Cavanaugh',
                expanded: true,
                children: [
                     {label: 'Documents', leaf: false},
                     {label: 'Downloads', leaf: false},
                     {label: 'Movies', leaf: false},
                     {label: 'todo_list.txt'}
                ]
      }]
 }]
}).render();
 
Let's look at what's going on here. First we're doing a traditional object construction in JavaScript, which is creating a new object. You're saying give me a new TreeView, and let's call it "users".
 
So what's with that .render() at the end?
 
That render() does not have to be called until you're absolutely ready to display the widget. In fact, there are many times where you may wish or need to configure a widget, but only render it under certain circumstances, or after a certain period of time has passed.
 
But if you don't care about waiting, you can just do it all inline (render will still return a reference to the widget).
 
We've just created a tree widget, and have this users variable, so what? Why is this exciting if you're just *using* widgets?
 
Because even if you're just using widgets, you can still do VERY interesting stuff with these widgets. Remember how we talked about working with elements?
 
The widget API is VERY similar to the Node API, meaning that in the same way that you can do get/set for properties on a Node, you can also do that on a Widget. And in the same way that you can use .on() for nodes, you can also do the same thing for Widgets.
 
But before we jump into that, let's look at another widget, something like Tooltip.
 
So let's go ahead and create a Tooltip. We'll use the aui-tooltip module:
var tooltip = new A.Tooltip({
     trigger: '.use-tooltip',
     bodyContent: 'Hello World!'
}).render();
 
What we're doing here is saying give us a new Tooltip, and show it when you hover over any item with the css class of .use-tooltip. It's also saying to show the content of the body to be "Hello World!". Now, in most cases, this wouldn't be super helpful because you wouldn't want to have the same tooltip text for multiple items, but there is a way to easily have it read the title attribute of the element instead. I'm simply manually using the bodyContent to illustrate the next part.
 
So now we have our tooltip object, what can we do to it? Well, all of those things we passed into it, like trigger, and bodyContent can all be read or changed using get/set, and not only that, but you can listen in to when they're changed as well.
 
This might take a second to realize just how cool this is, but take my word for it, it's insanely powerful.
 
Let's take a look. We'll go ahead and change the message of the tooltip to instead say "Hey there Earth!".
 
tooltip.set('bodyContent', 'Hey there Earth!');
 
Now when you hover over it, the tooltip will say 'Hey there Earth!'.
 
Moderately cool, but the real power comes from the change events that are used with this. Whenever you use get or set, the widget fires a custom event for that change.
So in this case, the event that got fired was "bodyContentChange". Every attribute on the widget fires a "change" event when it gets changed, and you can listen to it in two phases.
 
You can basically listen to the attribute get changed before the value is changed, and prevent it if you want, or after it gets changed.
 
This took me a few minutes to sink in, but here are the benefits:
 
Even as a person using the widgets, you have the ability to listen to interesting moments of what's going on and manipulate widgets on the page.
 
Here's an example: 
All widgets have an attribute called "visible", and when this attribute is changed, the widget hides or shows. Usually you just call widget.hide() or widget.show().
 
But let's say on our tooltip, we want to listen before it shows, and if a certain element is missing on the page, we will prevent the tooltip from being shown.
 
We would do:
 
tooltip.on('visibleChange', function(event){
     if(!A.one('#myEl')){
         event.preventDefault();
     }
});
So now, the tooltip will only show up if an element with the id of myEl is on the page.
 
Or here are some more practical examples:
 
Let's say you're creating a Dialog, and you toggle the visibility of the dialog based on if a separate ButtonItem widget is active?
 
You would use the aui-button-item widget, and the aui-dialog widget, and do:
 
var dialog = new A.Dialog({title: 'Hello', bodyContent: 'Lorem Ipsum'}).render();
var buttonItem = new A.ButtonItem({active: true, label: 'Toggle Dialog'});

buttonItem.after('activeChange', function(event){
 dialog.set('visible', event.newVal);
});

Or what about if you have a ColorPicker widget, and want to update the value of a text input when the hex code changes? You can even pass in the events when you create the widget.
 
You would use the aui-color-picker
 
new A.ColorPicker({
     after: {
         hexChange: function(event){                             A.one('#myInputNode').val(event.newVal);

         }       } }).render();
Another thing I mentioned last time was that Widgets can be plugged just like Nodes. Well, remember our handy dandy IO plugin? We can plug any widget with it, and it will automatically know what elements it should grab internally.
 
For instance, let's create another dialog again, and let's plug it: 
var dialog = new A.Dialog({title: 'Hello World'}).plug(A.Plugin.IO, {uri: 'test.html'}).render();
The plugin will smartly grab the area where the content goes, but not the area of the titlebar, and add a loading mask, and insert the content of the ajax request into there.
 
A couple of notes:
 
The .render() method, will, by default, add the widget into the body element. But if you pass it either a selector or a element, it will render the widget into that element on the page.
 
So I could have easily done this:
new A.ColorPicker().render('#myContainer');
 For Widgets, if you attach a listener "on" that event, it fires before the change has been made, so you can think of it as a "before" stage. It's kind of confusing, but it works.
If you listen to the "after" stage, it's after the work has already been done.
In most cases, you'll only care about being notified after, unless you want to prevent something from happening, or want to know before it happens.
90% of the time, I use "after" to listen to widget events.
 
Anyways, that's it for now on using the widgets. Next time, we'll talk about how you can make your own widget, and what a widget is actually made of.
See you then!

AlloyUI - Working with Plugins

March 24, 2010 By Nate Cavanaugh

 I mentioned last time that I would talk about the IO Plugin that we have that makes one of the most common tasks of updating content.

So first, I'll show you how to use it, then we'll talk more about how to make your own plugin.
 
So let's set up our sandbox:
AUI().use('io-plugin', function(A){
 // Our code will go here
});
 
So the IO plugin is essentially everything that A.io.request is. All the same arguments, same behavior, but what it does for you is kinda cool.
 
There is a common pattern we kept seeing in our ajax calls which was:
 
  1. Insert loading icon into region
  2. Do ajax call
  3. On success, update region with new content
Here is what the code would look might like before:
 
var contentNode = A.one('#contentNode');

if(contentNode) {
  contentNode.html('<div class="loading-animation"></div>');

  var myRequest = A.io.request('test.html', {
    on: {
      success: function(event, id, xhr) {
        contentNode.html(this.get('responseData'));
      }
    }
  });
}

It's a trivial process, but when you see it so often, you start to think that it's one of those patterns that could imagine yourself sitting in a padded room chanting over and over.
 
So we created a handy little plugin that handles this for us, and in a much nicer way. What this plugin does is does an ajax request the same as A.io.request, but also adds a loading mask to the area that we want to load content into. As soon as the content is ready, it parses through it and looks for any javascript that may be inside of it and executes it, and sets the content into our area.
 
var contentNode = A.one('#myNode');

if(contentNode) {
 contentNode.plug(A.Plugin.IO, { uri: 'test.html' });
}
And that's it.
 
Here's what it looks like:
 
It basically will mask any existing content, and add a loading indicator that is centered above it.
 
Here is some of the cool stuff about the plugin (and all plugins in Alloy):
 

1. It has it's own namespace

This means that this plugin lives inside of it's own area on the node so that it won't clobber with other plugins. Here's a good reason. A.Plugin.IO inherits all of the methods and properties that are on A.io.request, so you can call things like .start(), .stop() (to start and stop the request of course), or .set('uri', 'new_test.html'), or .set('dataType', 'json') and everything else we covered in the <a href="http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-ajax?_33_redirect=%2Fweb%2Fnathan.cavanaugh%2Fblog">last post</a>.
If all of that was placed on the main object, then it would conflict with any methods that might exist already on that node, or maybe another plugin.
So instead, it's placed in a namespace, and you can access that like so:
 
contentNode.io
 
So if you want to set the dataType on the plugin to json, you can do:
 
contentNode.io.set('dataType', 'json');
or if you want to stop the connection:
contentNode.io.stop();

2. Plugins can be "unplugged"

This is incredibly useful if you're writing a plugin that should do some clean up work when a user is finished with it (for instance, if you have a plugin that adds in some children elements or adds on some class names to a container).
 
You would just call:
contentNode.unplug(A.Plugin.IO);
 

3. Plugins can be plugged to NodeLists as well as Nodes

So this would work as well:
 
var contentNodes = A.all('.content-nodes');

contentNodes.plug(A.Plugin.IO, { uri: 'test.html' });
 
Then we could grab the first item in the NodeList and access the plugin namespace
contentNodes.item(0).io.set('cache', false);
 

4. Plugins can also be on Widgets

I'll cover widgets more next time, but the same exact process applies, and in fact, the IO plugin is written in such a way that it knows whether it's in a Node or a Widget and will behave accordingly.
 

5. Plugging doesn't have to be a per instance affair.

You can do this:
A.Node.plug(A.Plugin.IO, {autoLoad: false, uri: 'test.html'});
Now you could do:
var contentNode = A.one('#contentNode');
if(contentNode) {
 contentNode.io.start();
}
 
The difference is that since we called A.Node.plug() (which is a static method on the Node class), it plugs all newly created instances with that plugin.
 
I recommend doing it on a per instance basis, however, simply because 1, you'll consume less resources, and two, you don't have to worry about if your existing objects have been plugged.
 
6. You can plug with multiple plugins at once.
So for instance, you can do this:
 
contentNode.plug([
 { fn: A.Plugin.IO, cfg: {uri: 'test.html'} },
 { fn: A.Plugin.MyOtherPlugin }
]);
If that looks confusing, feel free to ignore it, but it simply is a way to pass in mutliple plugins and their configurations (if they need one) all at once.
 

Creating a plugin

 
What's the simplest way to get started creating a plugin? Well here's what's to remember: A plugin, at the very least, is a function, with a property on it called NS which will be it's namespace.
 
So for this example, I'm going to create a plugin that takes an input field, and inserts a "defaultValue". When you focus the field, if the value matches the "defaultValue", it will empty the field, and allow the user to enter their value. When the user moves away from the field, if they haven't entered anything new, it will add in the default text.
 
If you wish to jump to the demo, go ahead and take a look here: Plugin Demo.

 

 
I'm going to start with this markup:
 
<input data-defaultValue="Enter Text" id="myInput" type="text" value="" />
 
HTML5 allows for custom attributes if you prefix the attribute with "data-", so you'll notice I added a new attribute called "data-defaultValue", which our plugin will read.
 
So I'll create the javascript:
 
var defaultValuePlugin = function(config) {
    var node = config.host;

    var defaultValue = node.getAttribute('data-defaultValue');
    var startingValue = node.val();

    if (!startingValue) {
      node.val(defaultValue);
    }
    node.on('focus', function(event) {
      var value = node.val();

      if (value == defaultValue) {
        node.val('');

      }

    });

    node.on('blur', function(event) {
      var value = node.val();

      if (value == '') {
        node.val(defaultValue);

      }
 });
};

defaultValuePlugin.NS = 'defaultValue';

Now all we have to do to get it working is simply plug it onto a node:
 
A.one('#myInput').plug(defaultValuePlugin);
 
I'll go over some points of the code above.
 
One is that, notice that the first line points to config.host. The argument config is the configuration object that is passed into the plugin, but by default the host is always passed into the plugin, so you always have access from the plugin to whatever is being plugged.
It's like a magic link to whatever item you're plugging.
 
The next lines I'm doing the basic work getting an attribute, setting a value if one hasn't been set, and in the bulk of it, attaching focus and blur listeners to do the checking for the value.
 
On the last line, I'm attaching a property called NS to the function that we created. This is the namespace that this plugin will live under, and even if we don't need to access anything specifically, it's there so we can plug something without worrying about it colliding with any other plugins.
 
This is really just scratching the surface of the power that the plugin system offers, but I wanted to show a simple case, rather than bog down in the mire of complexity. If there is any interest in seeing advanced plugins, I can always write an 11th blog post, but the YUI3 page also offers a lot more info if you would like to investigate further as well.
 
Until next time, see you guys later!

 

AlloyUI - Working with Ajax

March 17, 2010 By Nate Cavanaugh

Ajax is one of those patterns that are a must have with a UI framework, so let's go ahead and jump right into doing some Ajax requests, and then we'll dive into the more complex cases.
 
Let's prep our sandbox, but this time, the module we're going to use is called "aui-io-request".
 
AUI().use('aui-io-request', function(A){
 // Our code will run here
});
 
The simple of the simple, let's assume we're just going to be making a simple ajax request to a plain html file, called "test.html".
 
A.io.request('test.html');
 
That's all there is to it. However, that's not very interesting because it doesn't do anything.
 
Let's say want to send a POST request to the server:
 
A.io.request('test.html', {
   method: 'POST',
   data: {
     key1: 'value'
   }
});
 
How about responding to the server? There are 5 possible callbacks: start, complete, success (or) failure, and end.
 
If I wanted to alert the response from the server, here's what I would do: 
A.io.request('test.html', {
  on: {
   success: function() {
     alert(this.get('responseData'));
   }
  }
});

What is this.get('responseData')? It's basically a normalized property of what is returned from the server. It's useful because A.io.request supports having different types of data returned by the server and automatically handled.
For instance, if your server returns JSON like {"myProperty": 2}, you could do something like:
 
A.io.request('test.html', {
  dataType: 'json',
  on: {
   success: function() {
     alert(this.get('responseData').myProperty); //alerts 2
   }
  }
});
 
You can also work with XML that way. Assuming your server returns something like: <name>AlloyUI</name> you could do:
A.io.request('test.html', {
  dataType: 'xml',
  on: {
    success: function() {
     alert(A.all(this.get('responseData')).all('name').text()); // alerts AlloyUI
    }
  }
});

You can also submit all of the data in a form via ajax as well. Here's the simplest version: 
A.io.request('test.html', {
  form: {

      id: 'myFormId'

  }
});
 
That will serialize all of the data in the form, and send it to "test.html".
 
One other handy feature of this is that you can define an ajax connection once, and reuse it multiple times, and start and stop it later on.
Here's an example: 
var myAjaxRequest = A.io.request('test.html', {
    method: 'POST',
    data: {
      key1: 'value1'

    }

});

Now later on, if I want to make that same ajax call again, all I have to do is call:
 
myAjaxRequest.start();
 
But what if I want to just define the call, but not execute it the first time (for instance, you know you want to run it later, but you don't want to update the server), you can do:
 
var myAjaxRequest = A.io.request('test.html', {
 autoLoad: false,
 ...
});
 
What's cool about this is that if later on, you want to change one of the properties before you send the request, you can do that as well. For instance, let's say you want to disable caching before you start the connection again:
 
myAjaxRequest.set('cache', false);
 
Or if you wanted to change from POST to GET
 
myAjaxRequest.set('method', 'GET');
 
Or change the dataType to JSON:
 
myAjaxRequest.set('dataType', 'json');
 
Or even change the URI at the last moment:
 
myAjaxRequest.set('uri', 'new_test.html');
 
Then when you're ready you would call:
 
myAjaxRequest.start();
 
And if at any time after you have started the request, you want to stop the whole request, you can call:
 
myAjaxRequest.stop();
 
And that's most of it right there. There are some cool plugins that we have that make working with ajax easier, but since the next topic is on plugins, I'll cover those in the next blog post, and they'll be a nice segue between topics.
 
One of those plugins is called A.Plugin.IO, and it's incredibly awesome, because it simplifies the extremely common task of not only loading content into a node or a widget, but adding a loading indicator to that node and automatically parsing the javascript for you.
 
I'll go into more details in the Plugins post, but it's really handy.
 
See you then!
Showing 1 - 5 of 34 results.
Items 5
of 7