« Back

AlloyUI - Working with elements and events

Staff Blogs March 16, 2010 By Nate Cavanaugh Staff

Getting Started

Welcome to our first post in talking about Alloy. I'm going to jump right in, but the only piece of info I want to cover beforehand, and you'll see me do it in every post, is the idea of "sandboxes". Because AlloyUI is built on top of YUI3, it has the concept of the sandbox. What this means is simply a callback where you run your code.

The way it's constructed is that you declare the packages that you want to use, and then, inside of your sandbox, you use them.

The benefit to this is that it allows your code to run as lean as possible, and loading only what it needs, without having to load a lot of stuff on the page first.

How do you create a sandbox?

Simple:

AUI().use(function(A) {   // Your code goes here  });

Let's look at that real quick.

AUI() is a function call, and you attach a .use on it. Inside of that .use(), you can pass 1-n number of arguments, but the last argument *must always be a function*.

You'll notice that the callback gets an argument passed to it called "A". That A is *the* Alloy object. It's where all of Alloy objects and classes are stored.

 

Most of the time you'll be setting up your sandbox using at least one or two packages. Here's how that would look like using the event and node packages:

AUI().use('event', 'node', function(A) {
   // Your code goes here
});

When you see me write code samples where I do something like:

A.one('body');

assume that I am inside of the sandbox.

Working with elements and events

The most common task you're most likely to come across in web development is working with elements on the page, and doing something with them. AlloyUI, because it's built on top of YUI3, has two objects for working with elements on the page, Node and NodeList. These two objects are closely related, and in fact, you can think of them as almost the same. The only difference is that Node is a single item, and NodeList is a collection of Nodes.

There are two methods you can use to get elements on the page, and each will return something different depending on the method.

The methods are called A.one() and A.all().

A.one() will return a Node object if it finds the element on the page (null otherwise), and A.all() will always return a NodeList (if it doesn't match any elements, it will return an empty collection).

 

Here's a few examples of how A.one() would be used:

var el_1 = A.one('#myCustomElement1');

var el_2 = A.one('.custom-element');

var el_3 = A.one('input[type=checkbox]');

Notice how A.one() will accept selector strings that are not just for ID elements? What if there are multiple elements matching the selector? It will just return the first one. This is useful in many situations, and has an impact on performance.

If the selector cannot be matched on the page, then A.one() will return null. This means that in order to operate on the Node element, you have to first do an if check on the variable then do the work.

For instance:

var el_1 = A.one('#myCustomElement1');

if(el_1) {

 el_1.setStyle('height', 50);

}

This can seem a bit verbose to some people, so it could be avoided if you wish. You could write the above like so:

A.all('#myCustomElement1').setStyle('height', 50);

without risk of throwing an error.

So why do I prefer A.one()? Mainly because of performance. A.one() will run about 2-4x faster to grab the element, but it also helps me write out clearer code (I know that if I'm not updating a block of code it's because it didn't find an element, whereas trying to debug long chains of code is a nightmare).

But both methods are there for you.

What kind of selectors are available? By default, anything in CSS2, which covers 98% of the cases and most of the selectors I've needed to write.

However, there is a CSS3 module, and if you need to do something like:

A.all('.custom-element > div:nth-of-type(even)'), just add the "selector-css3" module to your sandbox, and the selectors are available to you.

It's pretty rare that we've actually *needed* these selectors, though, but again, they're there if you need them.

That covers the basics on getting the elements, what about doing something with them?

So, let's cover some common tasks:

Setting styles

I'm going to grab my element:

var nodeObject = A.one('#myElement');

Setting a background color:

nodeObject.setStyle('backgroundColor', '#f00'); //Sets the background color to red

Setting a border 

nodeObject.setStyle('border', '5px solid #0c0'); //Sets a large green border

But what if I want to set multiple styles all at once? Just simply use the setStyles method (notice the "s" on the end of the name?).

nodeObject.setStyles({
   height: 200,
   width: 400
});

You can also get the current style for an element by doing something like:

nodeObject.getStyle('border');

One common task I think we've all done is to try to position a box somewhere on a page? Usually we'll just set the styles on the element, including the positioning.

For instance, let's say we wanted to move something to exactly 100px from the left, and 200px from the top.

Usually we might do something like:

nodeObject.setStyles({
  left: 100,
  position: 'absolute'
  top: 200
});

But then, what happens if it's inside of a positioned container? It will be relative to the container, then your offset will be off.

Instead, here's how you would do it now:

nodeObject.setXY([100, 200])

And it will automatically calculate the parents positioning for you guaranteeing that it's at the spot absolutely on the page that you want it. Much shorter code and much more accurate.

But what is really cool that is related to this is, often times you just want to center an item on the page absolutely. Here's how you would do it:

nodeObject.center();

Working with class names

All of the most convenient ways of working with class names, and then some, are here:

nodeObject.addClass('custom-class');

nodeObject.removeClass('custom-class');

nodeObject.toggleClass('custom-class');

nodeObject.replaceClass('custom-class', 'new-class');

nodeObject.hasClass('custom-class');

nodeObject.radioClass('custom-class');

In that last line, radioClass() will remove the class name from all of the sibling elements, and add it only to the current item, similar to how a radio button would behave.

 

Manipulating elements

Appending a new element to the nodeObject:

nodeObject.append('<span>New Text</span>');

Appending the nodeObject to another element already on the page:

nodeObject.appendTo('body');

Updating the innerHTML of an element:

nodeObject.html('<b>new text</b>');

Removing an element:

nodeObject.remove();

Creating a brand new node from scratch:

var newNodeObject = A.Node.create('<div id="myOtherElement">Test</div>');

Moving up and down the elements

Often you need to jump around to different elements relative to the current one you're on (for instance, to find a parent of a current item or a child/children).

Finding the first parent of nodeObject with the class name of .custom-parent:

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

Finding the first child with the class name of .custom-child:

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

Finding all children with the class name of .custom-child:

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

It's interesting to note that most of the methods that are on Nodes are also on NodeList. The ones that aren't are usually just the getters where it wouldn't make sense for a collection of items to return the data from any one item.

Meaning this: it makes sense to have a collection, like nodeListObject, which contains 5 div elements, and when you call nodeListObject.setStyle() for that style to be applied to all 5 elements, or if you call nodeListObject.append('<b>test</b>') for it to append a new b element to every item.

But it doesn't make much sense to do: nodeListObject.getStyle('backgroundColor'). What should it return? The first item in the collection? The last item?

And since it's insanely easy to do this instead:

nodeListObject.item(0).getStyle('backgroundColor')

it just makes more sense not to add the methods onto the NodeList to avoid confusion when getting data out of an element.

Getting properties

Now, here comes a really interesting part. Since nodeObject is a wrapped element, you can't just do nodeObject.id to get the id or nodeObject.parentNode. If you tried that, it would return undefined.

Instead, we do nodeObject.get('id') or nodeObject.get('parentNode').

Here's what is REALLY cool about using the getter: nodeObject.get('parentNode') will return another wrapped Node object, and if it's a collection, like nodeObject.get('childNodes'), it will be a wrapped NodeList object.

So all of the DOM properties are available.

EVEN cooler:

get will accept a dot (.) separated list of properties and traverse it for you. So let's say you know you have an item exactly three parents up, and want to set the background color to red:

nodeObject.get('parentNode.parentNode.parentNode').setStyle('backgroundColor', '#f00');

Interaction time

We've touched on how to wrangle the elements on the page. What about adding an event to it, such as doing something when a user interacts with it?

It's actually pretty simple. Every Node and NodeList has a method called on() that let's you, appropriately enough, do something "on" that event.

Let's say I want to alert "Hello" when a user clicks on the nodeObject

nodeObject.on('click', function(event){
   alert('hello');
});

Or let's say we want to add a border when a user first moves their mouse over an item:

nodeObject.on('mouseenter', function(event){
   this.setStyle('border', '5px solid #555');
});

Notice how the "this" object is used without wrapping it? It's automatically wrapped for you to be a Node object, which is incredibly convenient.

But what if, on the off chance, you *must* get the original DOM object. You can do nodeObject.getDOM() and it will return you the underlying DOM element.

This also applies to NodeList objects as well.

So if you do A.all('div').getDOM() it will return you an array of plain DOM elements.

What if about if you need to remove an event?

Let's say you do this:

nodeObject.on('click', myFunc);

you can detach the event by simply doing:

nodeObject.detach('click', myFunc);

or you could even just remove all events by not passing a second argument, like this:

nodeObject.detach('click');

What if you want to do some work on document ready?

You can do:

A.on('domready', function(event){
   // More work here
});

Now, there are times when you want to both load some modules and fire your callback on DOM ready, so here is how you would do that in Alloy:

AUI().ready('event', 'node', function(A){
   // This code will fire on DOM ready 
   // and when this modules are ready
});

Here's an interesting example. Let's say you want to listen on the node for only a specific key combination. For instance, you want to only fire the event when the user presses the escape key, but only when holding down the shift key.

Here's how you would listen to it:

nodeObject.on('key', function(event){
   // escape + shift has been pressed on this node
}, 'down:27+shift');

Now here's another use case some might be curious about. What if you want to prevent the default behavior of an event, for instance, if you want to stop a link's href from being followed?

nodeObject.on('click', function(event){
   event.preventDefault();
});

In Javascript, events bubble, which mean that by default, an even on one element also happens on every element that contain it, so if you click on a link, it will also fire an event on the body element as well.

You can stop your event from bubbling though, here's how:

nodeObject.on('click', function(event){
   event.stopPropagation();
});

You might notice that these are the same methods that exist in the W3C specification, but they're normalized to work the same in all browsers.

But there's also a shortcut if you want to just preventDefault and stopPropagation, which is like so:

nodeObject.on('click', function(event){
   event.halt();
});

Event delegation

Speaking of event bubbling, built into Alloy is event delegation. Event delegation is a technique that let's you can attach one event to a container but have it fire only on the children elements.

Imagine you have a list, and a lot of LI elements inside of it. You could add a new event listener for each element, but as your list grows, the number of listeners will also grow, as well as memory consumption.

And let's say you add elements via ajax, it's a pain to have to reattach events after every update as well.

So let's say go through an example. Let's say that we have this HTML:

<ul id="myList"><li>Test</li></ul>

Here's how we would use delegation:

var myList = A.one('#myList');
myList.delegate('click', function(event){
   alert(event.currentTarget.html());
}, 'li');

Notice a few things. One, we're calling a method called delegate, but it's very similar to "on". In fact, the only difference to the "on" method is that the third parameter is a selector that we will test to make sure the element matches before firing the function.

But also notice that we're referencing event.currentTarget. This is a property that will always point to the element that you are currently listening for, even inside of the "on" method, so I recommend using it.

But now that we've added our event, if you click on the list item, it will alert the contents of that item. Now let's try this:

myList.append('<li>Test 2</li>');

It will add another list item, and when you click on this new item, it will alert "Test 2", without having to reattach the event.

Conclusion

Hopefully this helps show you some of the helpful ways you can work with elements on your page, and help get you up to speed.

Threaded Replies Author Date
Nate, great post. Filled with lots of info but... Erik Andersson March 16, 2010 10:47 AM
Hi Erik, for custom events in Liferay, we're... Nate Cavanaugh March 16, 2010 4:50 PM
Hi Nate, I think this is a good start. I... Denis Signoretto March 16, 2010 10:59 AM
Great post, Nate. Where can we get more... Michael Young March 16, 2010 3:27 PM
Heya Mike, The YUI developer docs has some info... Nate Cavanaugh March 16, 2010 5:44 PM
<form method="post" name="tree" id="tree">... Manikandan S January 16, 2011 11:06 PM
For me, it looks like jQuery with few minor... Rafał Piotrowski March 17, 2010 1:59 AM
Hey Rafal, i agree with you, that looks realy... Markus Nordhaus March 17, 2010 8:09 AM
Hi Markus and Rafał, There are some patterns... Nate Cavanaugh March 17, 2010 9:00 AM
I understand. I wait for something which Alloy... Rafał Piotrowski March 18, 2010 2:32 AM
Great Post. It provides great insight for... Bavithra Rajendran March 18, 2010 2:46 AM
Pretty good! Thanks! Xinsheng Robert Chen May 1, 2010 2:52 PM
Please help! I'm writing test code in the... Ren Hoek May 18, 2010 12:03 PM
Hi Ren, This is because the aui fields generate... Nate Cavanaugh May 18, 2010 12:22 PM
Thank you! I had resorted to using a class... Ren Hoek May 19, 2010 9:17 AM
Hi Nate, Thanks for this great article. I... Jonathan Dray October 29, 2010 2:41 AM
Hi friend, <form method="post" name="tree"... Manikandan S January 16, 2011 11:08 PM
I have problems with selectors if I use a lot... Jakub Liska February 21, 2011 5:11 PM
Hi Jakub, The workaround would be using css... Erik Andersson February 22, 2011 12:24 AM
Thank you Erik, this is what I need. Btw is... Jakub Liska February 22, 2011 1:38 AM
There is no generic solution for this scenario... Erik Andersson February 22, 2011 2:12 AM
Great explanation Erik, thank you very much, it... Jakub Liska February 22, 2011 2:26 AM
So, also a couple of notes: 1. Almost all of... Nate Cavanaugh February 22, 2011 4:43 AM
Thank you Nate, and how would you create a... Jakub Liska February 22, 2011 5:18 AM
Hi Jakub, I'm planning on blogging about this,... Nate Cavanaugh February 22, 2011 5:43 AM
Well, I suppose that this doesn't answer the... Jakub Liska February 22, 2011 6:02 AM
Hi Jakub, That's actually a critical piece of... Nate Cavanaugh February 22, 2011 6:34 AM
Cool, I will do it like this. I was talking... Jakub Liska February 22, 2011 6:49 AM
Thanks for the article. It is extremely... Aston Chan March 25, 2011 11:56 AM
Hello Nate I am trying this code but this is... Muhammad Asif June 4, 2011 8:18 AM
If you look at ckeditor.jsp and... Jakub Liska February 22, 2011 6:18 AM
Hi, I am new to alloyui. This is my javascript,... Tech Geek June 19, 2011 11:23 PM
Nate, Thank you for the helpful article. I am... James Arvigo July 25, 2011 6:05 AM
[...] I sort of got it to work eventually... I... Anonymous July 31, 2011 8:54 PM
[...] I found a solution. Using the... Anonymous January 7, 2013 6:38 AM
[...] What we have done so far , Eclipse... Anonymous March 23, 2013 10:16 AM
Hi Nate, thank you for article. Sorry, but I'm... Faramarz Dorani April 7, 2013 2:16 AM
Hi, Nate I have a question for you, that how... Linh Le June 26, 2013 3:28 AM
sorry with the same comment: but i can still... Linh Le June 26, 2013 3:32 AM
[...] You may not be big fan of Yahoo Mail or... Anonymous August 24, 2013 8:43 AM

Nate, great post. Filled with lots of info but still hands-on. Definitely looking forward to the rest of the Alloy posts!

As for the events, is there in AUI an event similar to the allPortletsReady event? Working with portlets we can't always trust the domready event to do the job right?

Cheers,
Erik
Posted on 3/16/10 10:47 AM.
Hi Nate, I think this is a good start. I suggest to envolve a documentation process about using Alloy to ecourage people using it. Just a well organized wiki could be a good point of start.

Bye,
D.
Posted on 3/16/10 10:59 AM.
Great post, Nate. Where can we get more information about sandbox pattern?
Posted on 3/16/10 3:27 PM.
Hi Erik, for custom events in Liferay, we're still listening on the Liferay side.
In fact, we've augmened the Liferay object with the EventTarget class which means that it can listen to, fire, and have events bubbled to it.

So in order to listen for the allPortletsReady event, you would do:


Liferay.on('allPortletsReady', function(event) {
//Fires after all portlets have been loaded
});
Posted on 3/16/10 4:50 PM in reply to Erik Andersson.
Heya Mike,
The YUI developer docs has some info about it here: http://developer.yahoo.com/yui/3/yui/ but we handle it slightly differently in Alloy.

Basically, in YUI, each time you call YUI() it creates a brand new instance of YUI that's separate from each other (so the modules you load in one don't clobber the modules you load in another one).
However, in Alloy, we cache this instance that it creates so that every time we call it in the portal it's not burning performance on creating sandboxes.
However, we've done it in such a way so that it's pretty trivial to have AUI generate a new sandbox.

It's awesome because we get the benefits of development safety, while also enjoying better performance.

If there's anything specific you want to know, or more details, let me know and I can probably write up a separate blog post emoticon
Posted on 3/16/10 5:44 PM in reply to Michael Young.
For me, it looks like jQuery with few minor differences. Has AlloyUI any major advantage over jQuery?
Posted on 3/17/10 1:59 AM.
Hey Rafal,

i agree with you, that looks realy like JQuery (a realy great and simple lib).

@Nate Am i right, that AlloyUI adopt some patterns from JQuery and represents (in one view) a kind of "JQuery like wrapper" around YUI3?

By now, I have not realy understand why you chose YUI3 over JQuery. YUI3 is pretty big and JQuery is in my opinion very simple - but powerfull.

You are familiar with both (JQuery and YUI3). What is the major advantage on YUI3 over JQuery?

Markus
Posted on 3/17/10 8:09 AM in reply to Rafał Piotrowski.
Hi Markus and Rafał,
There are some patterns from jQuery represented in there. jQuery is an incredibly simple library, and while it is powerful in it's own right, it does have it's limitations.
One thing to mention, jQuery is primarily focused with working with the DOM. It has utilities for effects and ajax, but it's primary focus is getting something and doing something with it.

This first blog post is simply looking at one of the areas where Alloy and jQuery overlap. But there are many advantages to using Alloy (which is what the following blog posts will go more in depth on), but here are some of them:

-Dependency management of modules and lazy loading of resources (including loading of jQuery itself as a module).
-The ability for plugins to have their own namespace so they don't conflict with other plugins.
-Built in OOP features for augmenting objects, extending classes, etc
- Working with arrays
- A Widget system with a managed lifecycle, manage attributes, event system, and a well thought out markup architecture
- A CSS Framework
- The ability to run multiple sandboxed instances of Alloy on the page without conflicting with other instances on the page.

To me the best part is that if none of those interest you, you don't have to think about them, or even load them. The combined YUI core and Alloy core file is 9k, and that will lazy load every other file you need if you want to add other modules in.

With jQuery, you could probably find some plugins to do some of that functionality, but those plugins would all operate on the same global object, meaning that if someone else loaded a plugin with the same name later on in the page, something would break.

None of this is meant to disparage jQuery at all, as it will always have a special place in our hearts. But in an environment where applications can come from multiple sources (with multiple libraries being loaded), we needed to build on top of a foundation that had that in mind. We also needed to pull it into our own namespace that we could own so that we could guarantee that clients that build on top of it can be assured that we have final control of the API that we ship to them.

I hope that provides some insight, but of course, please feel free to let me know if you guys have any more questions emoticon
Posted on 3/17/10 9:00 AM in reply to Markus Nordhaus.
I understand. I wait for something which Alloy has and jQuery has not ;-)

Is this mean that jQuery will no longer be available in Portal (or it will be available as Alloy module)?
Posted on 3/18/10 2:32 AM in reply to Nate Cavanaugh.
Great Post. It provides great insight for getting started with AlloyUI. Thank You Sir, for a good start emoticon.
Posted on 3/18/10 2:46 AM.
Pretty good! Thanks!
Posted on 5/1/10 2:52 PM.
Please help!

I'm writing test code in the view.jsp file of the sample-jsp-portlet-5.2.0.1.war, and can't get even the simplest example from this post working.

The aui forms taglib elements render and work fine (and look nice), I have been able to get an aui-tree-view widget to render and work fine, but using your example, the following always alerts "nodeObject is null". Why? What is wrong? Please help.

<%
/**
* Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
...
*/
%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>

<portlet:defineObjects />

<script type="text/javascript" charset="utf-8">

AUI().ready('node', function(A) {
var nodeObject = A.one('#submitButton');
if(nodeObject) {
alert('nodeObject is NOT null');
} else {
alert('nodeObject is null');
}
});

</script>

<div id="textDiv">
<aui:input first="true" helpMessage="Enter text and click sumbit to send it to the server" name="ajaxText" label="AJAX Text" inlineField="true" type="text" />
<aui:button last="true" name="submitButton" value="Submit" />
</div>
Posted on 5/18/10 12:03 PM.
Hi Ren,
This is because the aui fields generate a portlet namespace for you by default (to help prevent collisions on the page).

If you do this:
var nodeObject = A.one('#<portlet:namespace />submitButton');

nodeObject should alert the button you're looking for.
We are actually working on a form plugin to help working with the forms easier so you don't have to manually pass in the portlet namespace on the JS side as well, but until then, it's helpful to remember that piece.

Thanks for the comment emoticon
Posted on 5/18/10 12:22 PM in reply to Ren Hoek.
Thank you!

I had resorted to using a class selector, which worked:

A.one('.aui-button-input').on('click', function(event) {
myAjaxRequest.set('data', 'name=' + A.one('.aui-field-input-text').get('value'));
myAjaxRequest.start();
});

...but this is much better:

A.one('#<portlet:namespace />submitButton').on('click', function(event) {
myAjaxRequest.set('data', 'name=' + A.one('#<portlet:namespace />ajaxText').get('value'));
myAjaxRequest.start();
});

Thank you so much!
Posted on 5/19/10 9:17 AM in reply to Nate Cavanaugh.
Hi Nate,

Thanks for this great article.

I have generated a form using the aui taglib. Now I want to dynamically add new fields in this form. (depending on a value selected by a user)
I have searched for some documentation on manipulating aui forms in javascript but I only found how to create a new form with YUI not how to modify an existing one.

Is there an alloy ui javascript library to manipulate forms ?
If it is the case could you please provide some examples or a documentation on how to use it ?

Thanks,
Jonathan
Posted on 10/29/10 2:41 AM.
<form method="post" name="tree" id="tree">
<body>
<input id="SecId" name="SecId" type="hidden" value=""/>
<table id="MainTable" width="100%" border="0" style="backgroundemoticonfffff;">
<div id="markupBoundingBox">
<li >
<span id="lastSelectedChange" value="<%=alloyVO.getSecId()%>"><a href="#"><%=alloyVO.getSecTitle()%></a></span>
</li>
</div>
</table>

<aui:script use="aui-tree-data">

AUI().ready('aui-tree-view', function(A) {

var treeView = new A.TreeView({
boundingBox: '#markupBoundingBox',
})
.render();

treeView.on("lastSelectedChange", function(e){
var id = e.newVal.get("id");
var secId=document.getElementById("SecId").value;
alert(secId);
var url = '<portlet:actionURL/>"&SecId"= +secId';
var jsonResult = A.io(url,callback);
});
var callback =
{
xdr: "json",
timeout: 5000,
on: {
success: function (x, o) {
var jsonResponse = o.responseText;
}
}
};
});

</aui:script>
</body>
</form>

This is my code, in span tag <%=alloyVO.getSecTitle()%> has one "id"(<%=alloyVO.getSecId()%>)
I need to pass this id to business class via url
How to pass that id to treeview function and business class can you help me

Otherwise how to create parent node and child node in alloy but both nodes are getting data from business class or database
can you guide
Posted on 1/16/11 11:06 PM in reply to Nate Cavanaugh.
Hi friend,
<form method="post" name="tree" id="tree">
<body>
<input id="SecId" name="SecId" type="hidden" value=""/>
<table id="MainTable" width="100%" border="0" style="backgroundemoticonfffff;">
<div id="markupBoundingBox">
<li >
<span id="lastSelectedChange" value="<%=alloyVO.getSecId()%>"><a href="#"><%=alloyVO.getSecTitle()%></a></span>
</li>
</div>
</table>

<aui:script use="aui-tree-data">

AUI().ready('aui-tree-view', function(A) {

var treeView = new A.TreeView({
boundingBox: '#markupBoundingBox',
})
.render();

treeView.on("lastSelectedChange", function(e){
var id = e.newVal.get("id");
var secId=document.getElementById("SecId").value;
alert(secId);
var url = '<portlet:actionURL/>"&SecId"= +secId';
var jsonResult = A.io(url,callback);
});
var callback =
{
xdr: "json",
timeout: 5000,
on: {
success: function (x, o) {
var jsonResponse = o.responseText;
}
}
};
});

</aui:script>
</body>
</form>

This is my code, in span tag <%=alloyVO.getSecTitle()%> has one "id"(<%=alloyVO.getSecId()%>)
I need to pass this id to business class via url
How to pass that id to treeview function and business class can you help me

Otherwise how to create parent node and child node in alloy but both nodes are getting data from business class or database
can you guide
Posted on 1/16/11 11:08 PM.
I have problems with selectors if I use a lot of jsp tag libraries like aui or liferay-ui, because in most of them I cannot set up my own "id" of the element - it is generated (aui_3_3_0_11336). And if I have a form with couple of columns and a lot of specific fields, it is hard to create a selector for concrete field. At least for a server-side programmer.
Posted on 2/21/11 5:11 PM.
Hi Jakub,
The workaround would be using css classes instead. For most (if not all) of the tag lib components you can specify cssClass. You can use this css class to target a specific element. Let's say that we have a specific form input that we have given the cssClass "my-form-input-1". Now, let's say that our form has a wrapper with id "myFormWrap". The markup would look something like the following (simplified):

<div id="<portlet: namespace />myFormWrap">
<aui:form ... >
<aui:input cssClass="my-form-input-1" ... >
</aui:form>
</div>

You would access the form field in the following manner:

<script type="text/javascript">
AUI().ready('aui-base', function(A) {

// Wrapper node
var myFormWrap = A.one('#<portlet:namespace />myFormWrap');

// Specific field
var formField = myFormWrap.one('.my-form-input-1');

});
</script>

Cheers,
Erik
Posted on 2/22/11 12:24 AM in reply to Jakub Liska.
Thank you Erik, this is what I need. Btw is there also a workaround for addressing elements from outside JSPs ? From included *.js file for instance. There is no way to get portlet namespace, so that it must be gotten by other means, which isn't easy on the client side.
Posted on 2/22/11 1:38 AM in reply to Erik Andersson.
There is no generic solution for this scenario since some portlets can be instanceable. We will get a couple of possible solutions.

First, a non-instanceable portlet. This portlet can only occur once on a page. Then we will be safe finding our first reference from the portlet css class, that is specified in liferay-portlet.xml (css-class-wrapper tag):

<script type="text/javascript">
AUI().ready('aui-base', function(A) {

// portlet node
var myPortlet = A.one('.my-portlets-css-wrapper');

// Specific field
var formField = myPortlet.one('.my-form-input-1');

// Do something with formField

});
</script>

Next, consider an instanceable portlet. This portlet can occur multiple times on a page. A.one will return the first element matching the provided selector that is found in the DOM tree. If we want to target both portlets we cannot use A.one, because then we would only target the first portlet found. Instead we need to use A.all, that returns a list of nodes (NodeList) instead of a single node. We can target each specific field in each portlet by iterating the NodeList:

<script type="text/javascript">
AUI().ready('aui-base', function(A) {

// portlet node list
var myNodeList = A.all('.my-portlets-css-wrapper');

myNodeList.each(function() {
var currentNode = this;

// Specific field
var formField = currentNode.one('.my-form-input-1');

// Do something with formField

});

});
</script>

Finally, instead we want to target a specific instance of this instanceable portlet only. Now we would have to know some more information about which instance to target. To do this we could use the id provided to the portlet at runtime. To find this id we can Firebug our page and find what id is given to it. This id will include the portlet id and look something like "p_p_id_56_INSTANCE_6Kkp_" where the last characters (6Kkp in my example) is a suffix identifying the current portlet instance. This, however, could be a bit tricky since code is typically written before the portlet is in place on a production server. On the other hand, this final example is not that common, at least not in my experience.

Hope that helps you.

Cheers,
Erik
Posted on 2/22/11 2:12 AM in reply to Jakub Liska.
Great explanation Erik, thank you very much, it helped a lot.
Posted on 2/22/11 2:26 AM in reply to Erik Andersson.
So, also a couple of notes:

1. Almost all of the taglibs in the AUI namespace allow you to pass in an id that will apply to some element (usually the root) in the component (and that will be namespaced automatically).
Also, the autogenerated ID's are only applied if there is no existing id on that element (so if you add an ID to some element, we'll never overwrite it).
Using a combination of id and classes is usually the fastest, something like:
var node = A.one('#p_p_id_56_instance_6Kkp_');
var contentNode = node.one('.aui-widget-content');
var listNodes = node.all('li.list-item');

etc.

2. One other way to pass in the instance is to use the sandboxed method of instantiation.
Doing this from your portlet:
AUI({namespace: '<portlet:namespace />'}).ready('my-custom-module');

Let's say inside of my-custom-module you have:

AUI.add('my-custom-module', function(A){
var namespace = A.config.namespace;
//... do other work here....
}, '', {
requires: ['aui-base']
});

So basically, using the sandboxed method, you can pass into custom configuration data into just your instance, like the portlet namespace, and that will be available inside of A.config.

(The only caveat is that you're running a fresh instance of Alloy, so any modules that Alloy has loaded up won't be in your copy. This is also a benefit, though, if you want to load modules that might have the same name, or somehow collide with the ones loaded by default in Alloy).

Just another way you could keep the code decoupled.

Hope that helps emoticon
Posted on 2/22/11 4:43 AM in reply to Jakub Liska.
Thank you Nate, and how would you create a callback function for ckeditor.jsp ? the iframe <body><script/></body> script seems to be executed always before parent document <scripts> (depends on type of browser) and even scripts referenced in header section. So that I cannot do something like this:

var getWordCount;

AUI().use('aui-node', 'console', function(A) {

getWordCount = function (htmlData) {
var target = A.one('div.my-png-image');
target.one('div:fist-child').set('text', strip(htmlData).trim().split(/\s+/).length);
};
});

Because getWordCount wouldn't be initialized in the time of iframe script execution.
Do you know what I mean? I mentioned that in the last comments of this blog post http://www.liferay.com/web/jonas.yuan/blog/-/blogs/scayt-and-ckeditor-in-liferay­-portal-6

Also the instanceReady event seems to work only in Firefox browser. I tried even in control panel - web content - ckeditor for creating articles.
http://issues.liferay.com/browse/LPS-15425

I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-)
Posted on 2/22/11 5:18 AM in reply to Nate Cavanaugh.
Hi Jakub,
I'm planning on blogging about this, but we actually created a function to work around issues like this.

It's probably one of the more confusing parts of not only Alloy, but asynchronous coding in general.

We created a method called Liferay.provide that will let you create a function that exists statically on the page (so you can call it at any time), but that will lazy load it's dependencies.

There is one caveat: you can't return data from it, because it's asynchronous. Luckily, your code snippet is only setting data, not returning it, so it shouldn't affect anything.

Here's how you would use it:

<aui:script>
Liferay.provide(window, 'getWordCount', function(htmlData){
var target = A.one('div.my-png-image');
target.one('div:fist-child').set('text', strip(htmlData).trim().split(/\s+/).length);
}, ['aui-base', 'console']);
</aui:script>

Then, if something calls getWordCount and passes in HTML data, it will automatically do a .use() for you inside of it, and it will also queue up each call and run all of the calls once it's finished loading, so that if you call it multiple times while it's loading, you'll have the most up to date data.

Basically, we use Liferay.provide whenever we need a function to exist all of the time, but need it to lazy load it's dependencies whenever it's called. This gives us the benefit of lazy loaded code, without the hassle of asynchronous issues you're experiencing.

I hope that helps emoticon
Posted on 2/22/11 5:43 AM in reply to Jakub Liska.
Well, I suppose that this doesn't answer the issue I was pointing out, it was regarding the order of JS execution. What I have observed, javascripts in html document within iframe are executed before scripts in parent window/html document. So that Liferay.provide( ) gets executed after the script in the iframe (ckeditor) and the function getWordCount doesn't exist yet.

I just tried... in chrome, iframe <head><script></head> executes after <script> in its parent document, but in Firefox, the iframe <script> is executed always before the parent <body><sript></body> and even before <head><script ref></body>.

If I declare a global function ( ) { ...... } in parent document, then it doesn;t matter that iframe scripts are executed first. But if I do something like Liferay.provide( ), it just must be executed before the iframe script...

I hope it makes sense :-)
Posted on 2/22/11 6:02 AM in reply to Nate Cavanaugh.
If you look at ckeditor.jsp and onChangeCallback() function, which calls the parent getWordCount() function. But it calls it in time, when Liferay.provide() was not called yet :-) The iframe script just executes before the parent scripts..which is perfectly ok if I'm calling gobal functions, but not If I depend on the fact, that my Liferay.provide( ) was executed before.
Posted on 2/22/11 6:18 AM in reply to Nate Cavanaugh.
Hi Jakub,
That's actually a critical piece of info that I missed, so I apologize. That really seems like a bug or a really big implementation difference.

But either way, in those cases, there is only 1 way to handle this, with 2 possible implementations.

When you can't guarantee load order, you have to poll for your result.

So you can either, from the parent html, poll the document to see if the CKEditor has been loaded in the child, or from the ckeditor.jsp poll to see when the parent javascript has loaded.

Here's how it would look like:
From the parent checking for ckeditor:
var getEditorInstance = function(){
// I forget the exact call for this, but it shouldn't be to hard to see the path to check for and put it in this file
return document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;
}
var editorInstance = getEditorInstance();
CKEditor's existence

var id = setInterval(function(){
if(!editorInstance){
editorInstance = getEditorInstance();
}
else {
getWordCount(htmlData);
clearInterval(id);
}
}, 50);

From the ckeditor.jsp it would be a bit simpler, you would just do:

var getWordCount = window.parent.getWordCount;
var id = setInterval(function(){
if(!getWordCount){
getWordCount = window.parent.getWordCount;
}
else {
getWordCount(htmlData);
clearInterval(id);
}
}, 50);

Basically, you just need to set an interval, check to see if the value exists, if it doesn't, try again, if it does, call your function, and clear the interval.

This is very weird that these scripts are executing with different timing, I'll have to look more into that.

Thanks Jakub,
Posted on 2/22/11 6:34 AM in reply to Jakub Liska.
Cool, I will do it like this. I was talking about that in IRC and the folks said that regarding iframes, the order of execution or access point of execution is kinda random. Sometimes it is executed first, sometimes not :-) Anyway the biggest problem in ckeditor.jsp is still the fact, that "ckEditor.on('instanceReady', ..)" doesn't work. It could be a bug in ckeditor. If you go to control-panel > web content - create article - and set a breakpoint in onChangeCallback( ), it is not called. Sometimes it is in Firefox, but never in chrome / opera.

Thank you very much for your help, a lot of things is much clearer to me.
Posted on 2/22/11 6:49 AM in reply to Nate Cavanaugh.
Thanks for the article. It is extremely helpful!

For some reason, I can't get my io.failure callback to trigger. I have the following code:

<aui:script>
// to work around allPortletsReady being called twice http://issues.liferay.com/browse/LPS-13548
var <portlet:namespace/>_calledAlready = null;

Liferay.on('allPortletsReady', function(event) {

if (!(<portlet:namespace/>_calledAlready)) {

<portlet:namespace/>_calledAlready = true;

YUI().use('io-base', 'node', function(Y) {

// function to handle response data.
var showAllBehaviors = function(id, o, args) {
var id = id; // Transaction ID
var data = o.responseText; // Response Data
var args = args;

alert('Inside showAllBehaviors: ' + data);
};

var handleFailure = function(id, response, args) {

alert('hi there');
/*
alert("The failure handler was called. Id: " + id + ".");

if (response.responseText !== undefined){
var s = "<li>Transaction id: " + id + "</li>";
s += "<li>HTTP status: " + response.status + "</li>";
s += "<li>Status code message: " + response.statusText + "</li>";

alert(s);
}
*/
­ };

// Subscribe to "io:success" event
Y.on('io.failure', handleFailure, Y, 'TransactionFailed');
Y.on('io.success', showAllBehaviors);

/* Configuration object for POST transaction */
var cfg = {
method: 'POST',
xdr: {'dataType' : 'xml'},
headers: {'Content-Type' : 'application/xml; charset=urf-8'}
};

var sUrl = "<liferay-portlet:resourceURL id="getAllBehaviors"/>";

// send ajax to get all behaviors
Y.io(sUrl, cfg);
});
}
});
</aui:script>

I know the server side is called but the io.success is not. So, I added the io.failure callback to check the return status. However, the handleFailure() never triggered. If I put a hard-coded alert('failure') in the Y.on('io.failure', handleFailure, Y, 'TransactionFailed'); instead of using the handleFailure function, then that alert is triggered.

Thanks in advance for any help.

-Aston
Posted on 3/25/11 11:56 AM in reply to Jakub Liska.
Hello Nate I am trying this code but this is not doing the job. In actuality the validator should itself halt the form submission but it does not do that so i wrote this code but it failed to and submitted the form. Please give your helpful comments.

<aui:script>


AUI().ready('aui-form-validator', 'aui-overlay-context-panel', function(A) {



var validator1 = new A.FormValidator({

boundingBox: '#<portlet:namespace />fm1',

//fieldContainer: 'p',
//errorClass: 'aui-form-validator-error',
//validClass: 'aui-form-validator-valid',
//containerErrorClass: 'aui-form-validator-error-container',
//containerValidClass: 'aui-form-validator-valid-container',
//showMessages: false,
//showAllMessages: true,
//validateOnInput: true,
//extractRules: false,
//extractCssPrefix: 'aui-field-',
validateOnBlur: true,
selectText: true,

rules: {
<portlet:namespace />bondamount: {
required: true
},
<portlet:namespace />email: {
required: true
},
<portlet:namespace />agencyBondTypeId: {
required: true
},
<portlet:namespace />principalId: {
required: true
}
},
fieldStrings: {
<portlet:namespace />bondamount: {
required: 'Bond Amount is a required field'
},
<portlet:namespace />email: {
required: 'Email is a required field'
},
<portlet:namespace />agencyBondTypeId: {
required: 'Bond type is a required field'
},
<portlet:namespace />principalId: {
required: 'Principal is a required field'
},
}
});


// Listening to validation events
validator1.on('validateField', function(event) {
// Fires when a field is evaluated

});
validator1.on('validField', function(event) {
// Fires when a field contains valid data

});

validator1.on('errorField', function(event) {
// Fires when a field contains invalid data

});

var myFunc = function(event){
alert(validator1.hasErrors());
if(validator1.hasErrors()){
alert("hello");
event.halt();
}

}

var nodeObject = A.one("#<portlet:namespace />fm1");
nodeObject.on("submit", myFunc);

});


</aui:script>
Posted on 6/4/11 8:18 AM in reply to Aston Chan.
Hi,
I am new to alloyui. This is my javascript,
$(window).load(function() {
$('#sample').myPlugin({ pauseTime:2000, pauseOnHover:false });
});
I tried in alloyui like this :
AUI().use('*', function(A) {
A.on("load", function () {
A.one(('#sample').myPlugin({ pauseTime:2000, pauseOnHover:false }));
});
});
How can i achieve the same in AUI ?

Regards
Posted on 6/19/11 11:23 PM.
Nate,

Thank you for the helpful article.

I am posting in the hopes for your feedback on strange behavior I am seeing when I adding 'aui-calendar' to the same page that already has a working 'aui-form-validator'. I have a perfectly working page with form and field validation events. I have a page with form and popup calendar for a date field.

However, as soon as I combine these on the same page, the form validation error messages no longer have any textual content.

Below is my normal form + validation event handler:

AUI().ready('aui-form-base', 'aui-form-validator', function(A) {
if (A.one('#myForm'))
{
var form = new A.Form({
contentBox: '#myForm'
}).render();
Etc. etc... on thru validtor logic
}

In Firebug inspecting the error messages when working normally show:

<label id="aui-3-1-1-1267" class="aui-form-validator-stack-error">
<div class="aui-form-validator-message required">This field is required.</div>
</label>

If I then update the event handler in no other way than to add 'aui-calendar' to the uses clause...

AUI().ready('aui-form-base', 'aui-form-validator', 'aui-calendar', function(A) {
Etc. etc... } // no other changes to to internal logic from here

The validation error messages, when inspected, now appear empty:

<label id="aui-3-1-1-1258" class="aui-form-validator-stack-error">
<div class="aui-form-validator-message required"></div>
</label>

As you can see ... the error message is now *GONE*.

Please help me understand... is there a work-around.
BTW... I tried creating 2 completely separate JS files, one for the form fields and validator, and another for the calendar. The same effect occurs. 'aui-calendar' completely knocks out 'aui-form-validtor' erorr messages now matter how used in the same page.

This is my 1st post here... I look forward to your input.

Thanks,

James in Los Angeles
Posted on 7/25/11 6:05 AM.
[...] I sort of got it to work eventually... I replaced the use of <aui:script> with just ordinary HTML's <script> tags. The portlet however, even after reload in Liferay, doesn't use the new code... [...] Read More
Posted on 7/31/11 8:54 PM.
[...] I found a solution. Using the 'activeTabChange' event. Instead of returning something I used 'event.preventDefault' described here AlloyUI - working with elements and events var goON = true;... [...] Read More
Posted on 1/7/13 6:38 AM.
[...] What we have done so far , Eclipse Installation Directory Structure Service Builder – I Service Builder – II and continuing to the last part . The MVC Framework and Alloy UI Every Portlet on a... [...] Read More
Posted on 3/23/13 10:16 AM.
Hi Nate, thank you for article.
Sorry, but I'm still confused about AUI. It doesn't work for me.
My Liferay version is 6.1.1. what shoud I write in my jsp file?
I wrote this in my jsp but it shows nothing:

<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<script type="text/javascript">
AUI().use('aui-button', function(A) {
new A.Button({
label : 'Basic',
srcNode : '#myButton'
}).render();
});
</script>
<button id="myButton"></button>

Please help me if it's possible for you.
tnx.
Posted on 4/7/13 2:16 AM.
Hi, Nate
I have a question for you, that how can use Alloy Ui 1.7 in life 6.1.1;
when i copy:
<script src="http://cdn.alloyui.com/1.7.0/aui/aui-min.js"></script>
to my portlet, the function run oke, but when i comment this:
<!-- <script src="http://cdn.alloyui.com/1.7.0/aui/aui-min.js"></script> -->
and download Alloy1.7.zip; i copy all of file in build forlder and replace in tomcat -->...>root --> html -> js -> aui;
but my function is not run;
i test with demo from:

http://alloyui.com/versions/1.7.0/examples/datatable/real-world/


Thanks!
Posted on 6/26/13 3:28 AM in reply to Faramarz Dorani.
sorry with the same comment: but i can still run oke with some function from 1.5 after paste new file on server;;
for example:
http://alloyui.com/versions/1.7.0/tutorials/
----------------------------­-----------------------------------
I have a question for you, that how can use Alloy Ui 1.7 in life 6.1.1;
when i copy:
<script src="http://cdn.alloyui.com/1.7.0/aui/aui-min.js"></script>
to my portlet, the function run oke, but when i comment this:
<!-- <script src="http://cdn.alloyui.com/1.7.0/aui/aui-min.js"></script> -->
and download Alloy1.7.zip; i copy all of file in build forlder and replace in tomcat -->...>root --> html -> js -> aui;
but my function is not run;
i test with demo from:

http://alloyui.com/versions/1.7.0/examples/datatable/real-world/


Thanks!
Posted on 6/26/13 3:32 AM in reply to Linh Le.
[...] You may not be big fan of Yahoo Mail or other Yahoo Services. But trust me YUI is something different from others. That’s the reason why Liferay Alloy UI is based out of YUI. So a default question... [...] Read More
Posted on 8/24/13 8:43 AM.