Skip to content
This repository has been archived by the owner on Dec 21, 2021. It is now read-only.
Nate Cavanaugh edited this page Apr 23, 2012 · 7 revisions

Welcome to the alloy-ui wiki!

To be filled out:

  • Getting Started

    About

    • What is AlloyUI and how does it relate to YUI3?
      • AlloyUI is the UI framework for Liferay Portal, and covers 3 main areas of the front-end stack: HTML, CSS, JavaScript.
      • YUI3 is the JavaScript foundation that we've built AlloyUI on top of. AlloyUI consists of more than 70 extensions, plugins, and widgets that leverage the YUI3 library, and extend its functionality. AlloyUI extends YUI3 to add helpful features to many different components (such as node, or selector), in addition to entirely new components (such as Image Gallery, IO or Editable).

    Core concepts

    • JS Primer, variables, objects, and functions There are some core concepts in AlloyUI that you must be familiar with the basics of JavaScript to grasp. If you don't know the difference between a String and a Function, or a Boolean and an Array, then it may be helpful to visit a site such as http://www.codecademy.com/tracks/javascript and get up to speed on a few of the basics.

      However, we'll try to cover some of the basic's here that might be confusing to people fresh to JavaScript:

      • Functions can be passed to other functions like any other value can. In JavaScript, functions are called "first class objects". This isn't really because they're hard-working go-getters, but more because they can be treated the same way as any other object. Which means even passing them to other functions or assigned to a variable. For example, you can do: var foo = function(bar){alert(bar)};. This means you can then do foo('test'); (see how we're creating a variable called foo and making it equal a function definition?). The other aspect is passing functions as arguments to other functions, which is how the whole concept of "callbacks" work. Let's take our previous example: var foo = function(bar){alert(bar)}; And let's add to it another function: var runFoo = function(baz, callback){callback(baz);};. In our new function, runFoo, we're creating a function that takes 2 arguments, baz and callback. All we're doing is executing callback as if it's a function (like we did with foo in the previous example). So if we were to do runFoo('test', foo); it would create an alert that says "test".
      • Not only booleans will evaluate to true or false. Because of its loosely-typed nature, things such as 0, an empty string '' or null will evaluate to false and pretty much anything else (except false of course) will evaluate to true. This means if you do: var foo = 'test'; if (foo) { alert('test'); }, then foo will evaluate to true and you'll get an alert. On the other hand, if you did var foo = ''; if (foo) { alert('test'); } then you'd never get an alert at all.
    • Note: Because AlloyUI is built on top of YUI3, many of the core concepts are inherited from YUI3 and apply equally to both. In fact, if you go to yuilibrary.com any of the examples or code snippets you find there will also work in AlloyUI.

    • "use"-ing AlloyUI functionality

      • One of the core concepts to YUI3 is the concept "using" modules. You can think of "using" a module as essentially requiring or importing a module, similar to other languages such as Python, Perl or Java. Traditionally in JavaScript, this is done via script tags, where you reference the script you want to add to the page and then in some other JS file loaded after your requirements (or in a script block later in the page), you use the code you want to execute.

      However, in YUI3 the way to load modules is to skip the part where you add the script tags. In fact the only script tag you'll ever have to add to your page is the one for the "seed" file (this is the bare minimum code needed to define the library).

      Let's say we wish to have a modal Dialog on our page. To do that, all we need to do is say: AUI().use('aui-dialog', function(A) { // A.Dialog is now available to use });

      Looking at what we have here, let's see what's going on:

        AUI()
      

      This call is what returns the Alloy object that we wish to work with. You'll notice it looks like a function call, and that's because it is. What it returns is an object that will contain the components and variables loaded by whatever components we decide to use.

        .use('aui-dialog',
      

      On the Alloy object that we get back from AUI(), there's a method called .use() which allows us to pass in any number of modules that we want loaded. For instance, we could always have done: AUI().use('aui-dialog', 'aui-editable', ....

        function(A) { ... }
      

      This function is the callback that will be executed whenever our modules are all loaded and ready to be interacted with. What is that A variable? That part is important. That is a reference to the Alloy object that is returned from our call to AUI(). This part is sometimes confusing for new users: why do we have an A variable and an AUI() call? Pardon us for a moment for a quick if somewhat technical explanation: YUI3 and AlloyUI can both be "sandboxed". This means that you could have multiple instances of the same YUI or Alloy object on the same page. What's the benefit of this? Let's say you have an application on your page and you want to use the sortable module. And let's also say that somewhere else on your page is another application, written by someone else, that also wants to use a Sortable module, but they're using a completely different plugin that has different methods and variables defined. YUI3 will sandbox these applications by default so that they can be each have their own set of modules loaded on them. AlloyUI however, operates slightly differently. By default, we cache this object. If you call AUI() 5 times on the page, it will always load on the same instance. This 1, prevents us from having to reload the configuration again on the same instance, but also allows us to behave as many users expect. You can however, easily switch the behavior of the AUI object. If you pass a config object to AUI(), like so: AUI({}).use('aui-dialog', ...), this will be a brand new instance of AlloyUI and will not have any of the current modules loaded into it.

      There are a couple of benefits of running this way: 1. You don't need to preload JavaScript on pages where the modules might not even be used. For instance, before AlloyUI, we have had cases where we needed to add a fairly large JavaScript component, such as a datepicker, to the global JS file because buried on some page of an app, it happened to use this one component. And slowly, over time, people would add new features, and it would increase the size of the JS file. 2. The JS to load the components won't block rendering of other elements. The modules that you're using, if you loaded via a regular script tag, would block all other elements on the page that were referenced after it. This allows you to load the interactable portion of the UI upfront with additional interactivity lazy-loaded. 3. Apps can avoid variable conflicts when each one loads up their own set of modules. 4. It helps produce more modular applications and components rather than giant monolithic ones that can't be reused.

      However, there are some "gotchas" when working with this sort of pattern to watch out for: 1. If you have UI that shouldn't be interacted with before the JS is finished loading, provide either a graceful degradation, or some sort of indicator that loading is happening. 2. There is also the common downside to this approach where you have to know which modules you should "use" and what functionality they provide ahead of time. We try to minimize this pain a bit though, which we'll go into later on.

    So, let's jump into some common things you may want to do in JavaScript. If you're already familiar with a library like jQuery, then you may find this Rosetta Stone helpful: http://deploy.alloyui.com/docs/ or you can keep reading.

  • Working with elements One of the most common tasks in web development is grabbing an element and doing something with it. To do this, YUI3 has a selector engine that leverages the same selectors available to you via CSS to interact with elements. In JavaScript, and in YUI3, there are two concepts to get familiar with: A single element (or node), and a collection of elements.

    • Finding elements To find a single element on the page, you can use A.one(). To find a collection of elements, you can use A.all(selector). Here are some examples of finding elements on the page:

      Grabbing the first div on the page

        A.one('div');
      

      Grabbing an element with the class of message

        A.one('.message');
      

      Grabbing an element with an id of message

        A.one('#message');
      

      Grabbing an element with a rel attribute set to message

        A.one('[rel=message]');
      

      Let's say you ran any of those methods and there were multiple elements with a class of message. If you use A.one it will give you the first one it finds. Often, you'll want all of the items matching it, in that case run A.all. Another key difference is that if A.one doesn't find an element, it returns null. However, A.all will always return a collection. If it doesn't find anything, it's an empty collection.

      Quick note: There is often some debate about which method to use or if there should even be two methods. The argument for using A.one even though you have to wrap subsequent calls with an if call, boils down to performance and application stability. The argument for using A.all is ease of use and fast development. You can use which ever you feel comfortable with, but we choose A.one more often than not, even with the if call. Why? Because 1, if an element does not exist where we think it should, code that relies on it existing later is likely to cause bugs if instead of returning null, you keep executing as if it is there. The other reason is because most of the time, we want a single element via ID, and this is much faster using A.one.

      Selectors

      In AlloyUI we've added extensions to the default YUI3 selectors that we borrowed the ideas for from jQuery and Sizzle. Most of the common ones from jQuery are there, such as :checkbox, :checked, :input, :button, :hidden, :text, :header, :visible, etc. This means you could get all of the checked checkboxes on the page with A.all(':checkbox:checked') or the first header (h1-h6) on the page with A.one(':header').

    • Traversing through elements Once you've grabbed the elements you want to work with, you'll often want to move around to other elements that are located somewhere in relation to the ones you currently have. And there are only 3 directions you'll really ever want to go: up, down, or across. Let's assume you have an element assigned to a variable called myElement.

      Moving down

      Very often, you'll want to find some children of the current element(s). This is pretty easy, as any object that you get back from A.one or A.all also has .one and .all methods, and they behave the same way. So let's say you want to find some child or children of myElement that has the CSS class of '.custom-child'.

      Grabbing the first child of myElement matching a selector

        myElement.one('.custom-child');
      

      Grabbing the all children of myElement matching a selector

        myElement.all('.custom-child');
      

      Moving up

      When you want to move up, there's only a couple of operations you want to do: grab the first ancestor you find, grab all of the ancestors of the current element, or grab an ancestor or ancestors that match a specific selector.

      Grabbing the first ancestor

        myElement.ancestor();
      

      Grabbing the first ancestor matching a selector

        myElement.ancestor('.custom-parent');
      

      Grabbing all of the ancestors

        myElement.ancestors();
      

      Grabbing all of the ancestors that match a specific selector

        myElement.ancestors('.custom-parent');
      

      Moving across

      There are often a couple of things you may want to do with elements on the same level as the current element(s) you have.

      Grabbing all siblings of the current element

        myElement.siblings()
      

      Grabbing all siblings of the current element matching a selector

        myElement.siblings('.custom-sibling');
      

      Grabbing the next element

        myElement.next();
      

      Grabbing the next element that matches a selector

        myElement.previous('.custom-sibling');
      

      Grabbing the previous element

        myElement.previous();
      

      Grabbing the previous element that matches a selector

        myElement.previous('.custom-sibling');
      
    • Attributes Often you'll need to retrieve some value from an attribute of an element.

      In AlloyUI we've added some helper methods to make it a bit easier:

      attr

      If you wish to get some attribute or property from an element, using .attr(name) will give you what you're looking for. This also removes the ambiguity between when you read the property and when to read the attribute.

      Getting the id of the element

        myElement.attr('id')
      

      Getting the checked state of an element

        myElement.attr('checked')
      

      It can also be used to set the attribute as well:

      Setting the id of the element

        myElement.attr('id', 'test');
      

      Setting the checked state of the element

        myElement.attr('checked', true);
      

      Setting multiple attributes at once

        myElement.attr({ id: 'test', checked: true });
      

      val

      We have also added helper method called .val() for getting and setting values on form elements.

      Getting the value of an input

        myInput.val();
      

      Setting the value of an input

        myInput.val('custom value');
      
    • CSS If you want to get or set styles of an element, it's easy to do with the getStyle and setStyle methods.

      Grabbing the background color of an element

        myElement.getStyle('backgroundColor');
      

      Grabbing the border of an element

        myElement.getStyle('border');
      

      Setting the background color of an element

        myElement.setStyle('backgroundColor', '#f00');
      

      Setting the border of an element

        myElement.setStyle('border', '1px solid #000');
      

      Setting multiple styles at once

        myElement.setStyles({ backgroundColor: '#f00', border: '1px solid #000' });
      

      height, width, outerHeight, innerHeight, outerWidth, innerWidth

      We also have custom methods for grabbing or setting the different heights/widths of an element.

      Grabbing the height of an element

        myElement.height();
      

      Grabbing the width of an element

        myElement.width();
      

      Grabbing the innerHeight of an element

        myElement.innerHeight();
      

      Grabbing the outerHeight of an element

        myElement.outerHeight();
      

      Setting the height of an element

        myElement.height(100);
      

      Setting the width of an element

        myElement.width(100);
      

      getMargin, getPadding, getBorderWidth

      It's often common that you'll want some piece of the box model from multiple sides at once. For instance, often we'll need to calculate the margin for the right and left side. You could do it with a lot of ugly code this way: var sideMargins = parseInt(myElement.getStyle('marginLeft'), 10) + parseInt(myElement.getStyle('marginRight'), 10); but this was a nightmare to constantly read and right.

      So instead, with any of these methods, getMargin, getPadding, getBorderWidth, you can pass in a string specifying the sides you wish to calculate and it will return a number with those totaled together. The sides are passed in as t,r,b,l (standing for: top, right, bottom, left)

      Some examples:

      Getting the horizontal padding of an element

        myElement.getPadding('rl');
      

      Getting the vertical margin of an element

        myElement.getMargin('tb');
      

      Getting three sides of the border width

        myElement.getBorderWidth('tbl');
      
    • Manipulating elements Another common task is to manipulate or modify elements.

      Appending a new element

        myElement.append('<span>New text</span>');
      

      Appending the current element to another element

        myElement.appendTo('#anotherElement');
      

      Grabbing the innerHTML of an element

        myElement.html();
      

      Setting the innerHTML of an element

        myElement.html('<span>New HTML</span>');
      

      Removing an element

        myElement.remove();
      

      Creating a new element

        var newElement = A.Node.create('<div>New Element</div>');
      
  • Events and adding behavior

    • Basic interactions
      • All Node and NodeList objects have a method called on() that can be used to specify the type of event and
    • Advanced interactions
    • Custom events
  • Ajax

    • Recieving data from the server
    • Sending data to the server
    • Datatypes - JSON, XML, and HTML
    • Polling
    • Misc. options
  • Effects

  • Plugins

  • Utilities

  • Components

    • Building your own component
    • Using an existing component
    • AutoComplete
    • Image Gallery
    • Dialog
    • Tabs
    • ...etc..
    • Misc. options