Erik Andersson 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Nate Cavanaugh Erik Andersson 14 Years Ago 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}); Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Erik Andersson 14 Years Ago 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}); Please sign in to reply. Reply as... Cancel
Denis Signoretto 14 Years Ago 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. Please sign in to reply. Reply as... Cancel
Michael Young 14 Years Ago Great post, Nate. Where can we get more information about sandbox pattern? Please sign in to reply. Reply as... Cancel Nate Cavanaugh Michael Young 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Mani kandan Nate Cavanaugh 13 Years Ago <form method="post" name="tree" id="tree"><body><input id="SecId" name="SecId" type="hidden" value=""/><table id="MainTable" width="100%" border="0" style="backgroundfffff;"><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 meOtherwise how to create parent node and child node in alloy but both nodes are getting data from business class or databasecan you guide Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Michael Young 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Mani kandan Nate Cavanaugh 13 Years Ago <form method="post" name="tree" id="tree"><body><input id="SecId" name="SecId" type="hidden" value=""/><table id="MainTable" width="100%" border="0" style="backgroundfffff;"><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 meOtherwise how to create parent node and child node in alloy but both nodes are getting data from business class or databasecan you guide Please sign in to reply. Reply as... Cancel
Mani kandan Nate Cavanaugh 13 Years Ago <form method="post" name="tree" id="tree"><body><input id="SecId" name="SecId" type="hidden" value=""/><table id="MainTable" width="100%" border="0" style="backgroundfffff;"><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 meOtherwise how to create parent node and child node in alloy but both nodes are getting data from business class or databasecan you guide Please sign in to reply. Reply as... Cancel
Rafał Piotrowski 14 Years Ago For me, it looks like jQuery with few minor differences. Has AlloyUI any major advantage over jQuery? Please sign in to reply. Reply as... Cancel Markus Nordhaus Rafał Piotrowski 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Nate Cavanaugh Markus Nordhaus 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Rafał Piotrowski Nate Cavanaugh 14 Years Ago 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)? Please sign in to reply. Reply as... Cancel
Markus Nordhaus Rafał Piotrowski 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Nate Cavanaugh Markus Nordhaus 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Rafał Piotrowski Nate Cavanaugh 14 Years Ago 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)? Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Markus Nordhaus 14 Years Ago 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 Please sign in to reply. Reply as... Cancel Rafał Piotrowski Nate Cavanaugh 14 Years Ago 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)? Please sign in to reply. Reply as... Cancel
Rafał Piotrowski Nate Cavanaugh 14 Years Ago 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)? Please sign in to reply. Reply as... Cancel
Bavithra Rajendran 14 Years Ago Great Post. It provides great insight for getting started with AlloyUI. Thank You Sir, for a good start . Please sign in to reply. Reply as... Cancel
Ren Hoek 13 Years Ago 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> Please sign in to reply. Reply as... Cancel Nate Cavanaugh Ren Hoek 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Ren Hoek Nate Cavanaugh 13 Years Ago 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! Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Ren Hoek 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Ren Hoek Nate Cavanaugh 13 Years Ago 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! Please sign in to reply. Reply as... Cancel
Ren Hoek Nate Cavanaugh 13 Years Ago 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! Please sign in to reply. Reply as... Cancel
Jonathan Dray 13 Years Ago 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 Please sign in to reply. Reply as... Cancel
Mani kandan 13 Years Ago 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="backgroundfffff;"><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 meOtherwise how to create parent node and child node in alloy but both nodes are getting data from business class or databasecan you guide Please sign in to reply. Reply as... Cancel
Jakub Liska 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Erik Andersson Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Erik Andersson Jakub Liska 13 Years Ago 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 nodevar myPortlet = A.one('.my-portlets-css-wrapper');// Specific fieldvar 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 listvar 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago Great explanation Erik, thank you very much, it helped a lot. Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Erik Andersson Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Erik Andersson Jakub Liska 13 Years Ago 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 nodevar myPortlet = A.one('.my-portlets-css-wrapper');// Specific fieldvar 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 listvar 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago Great explanation Erik, thank you very much, it helped a lot. Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Jakub Liska Erik Andersson 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Erik Andersson Jakub Liska 13 Years Ago 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 nodevar myPortlet = A.one('.my-portlets-css-wrapper');// Specific fieldvar 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 listvar 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago Great explanation Erik, thank you very much, it helped a lot. Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Erik Andersson Jakub Liska 13 Years Ago 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 nodevar myPortlet = A.one('.my-portlets-css-wrapper');// Specific fieldvar 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 listvar 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 Please sign in to reply. Reply as... Cancel Jakub Liska Erik Andersson 13 Years Ago Great explanation Erik, thank you very much, it helped a lot. Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Jakub Liska Erik Andersson 13 Years Ago Great explanation Erik, thank you very much, it helped a lot. Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Jakub Liska Nate Cavanaugh 13 Years Ago 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-6Also 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-15425I'd appreciate any help, I was fighting that yesterday a significant amount of time, I ended up drunk then :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Jakub Liska Nate Cavanaugh 13 Years Ago 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 :-) Please sign in to reply. Reply as... Cancel Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel
Nate Cavanaugh Jakub Liska 13 Years Ago 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 filereturn document.getElementById('iframeId').contentWindow.CKEditor.instanceReady;}var editorInstance = getEditorInstance();CKEditor's existencevar 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, Please sign in to reply. Reply as... Cancel Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel
Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel
Aston Chan Jakub Liska 13 Years Ago 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 Please sign in to reply. Reply as... Cancel Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel
Muhammad Asif Aston Chan 12 Years Ago 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> Please sign in to reply. Reply as... Cancel
Jakub Liska Nate Cavanaugh 13 Years Ago 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. Please sign in to reply. Reply as... Cancel
Tech Geek 12 Years Ago 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 Please sign in to reply. Reply as... Cancel
James Arvigo 12 Years Ago 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 hereThe 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 Please sign in to reply. Reply as... Cancel
Faramarz Dorani 11 Years Ago 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. Please sign in to reply. Reply as... Cancel Linh Le Faramarz Dorani 10 Years Ago 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! Please sign in to reply. Reply as... Cancel Linh Le Linh Le 10 Years Ago 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! Please sign in to reply. Reply as... Cancel
Linh Le Faramarz Dorani 10 Years Ago 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! Please sign in to reply. Reply as... Cancel Linh Le Linh Le 10 Years Ago 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! Please sign in to reply. Reply as... Cancel
Linh Le Linh Le 10 Years Ago 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! Please sign in to reply. Reply as... Cancel
(You) 10 Years Ago [...] 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 Please sign in to reply. Reply as... Cancel