-
Notifications
You must be signed in to change notification settings - Fork 675
Home
Welcome to the alloy-ui wiki!
To be filled out:
-
Getting Started
- 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).
-
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 dofoo('test');
(see how we're creating a variable calledfoo
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
andcallback
. All we're doing is executing callback as if it's a function (like we did withfoo
in the previous example). So if we were to dorunFoo('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''
ornull
will evaluate to false and pretty much anything else (exceptfalse
of course) will evaluate to true. This means if you do:var foo = 'test'; if (foo) { alert('test'); }
, thenfoo
will evaluate to true and you'll get an alert. On the other hand, if you didvar foo = ''; if (foo) { alert('test'); }
then you'd never get an alert at all.
- 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:
-
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 toAUI()
. This part is sometimes confusing for new users: why do we have anA
variable and anAUI()
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 toAUI()
, 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.
- What is AlloyUI and how does it relate to YUI3?
-
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 useA.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 useA.one
it will give you the first one it finds. Often, you'll want all of the items matching it, in that case runA.all
. Another key difference is that ifA.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 usingA.all
is ease of use and fast development. You can use which ever you feel comfortable with, but we chooseA.one
more often than not, even with theif
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 usingA.one
.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 withA.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
.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
orA.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');
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');
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:
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 });
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
andsetStyle
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' });
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);
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
- Basic interactions
-
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