Canceling Spry.Utils.loadURL request
Hi, i have posted a thread a few days ago concerning variables and Spry.Utils.loadURL and i wish to thank V1 i for the solution.
Now i wish to ask is it possible to cancel (or ignore) Spry.Utils.loadURL request while it is running.
By canceling i mean not the server side code, but the Spry.Utils.loadURL request.
Here is example of the problem:
I have some button that sens requests to the server. It is up to the complexity of every request how long it will take to responde (sometimes a minute).
So it is sometimes the case when a user sends some request and without waiting for the result sends another one.
Now i have multiple requests running in paralell and due to their asynchronous behaviour i do not know which one will display first (they use asame update panel).
So it will be good if i can tell the Spry.Utils.loadURL to cancel the "thread" he is running and only after that to start the new one.
Parallel possible solutions are disabling the button until the request responds, which is my very last undesired solution since i do not want to tye the users hand ....
Best regards:
Venelin
Spry.Utils.loadURL returns Spry's request object, so you could use that to cancel the running XHR request.
example;
var request = Spry.Utils.loadURL('GET','url.php',callback,true);
// abort the request:
request.xhRequest.abort();
Similar Messages
-
Spry.Utils.loadURL headers problem
I am posting a request with the loadURL function.
I am getting two values from a form :
- a WYSIWYG texarea editor which id is 'elm1'
- an input text field 'contentfilename'
In my textarea, I wrote a phone number that have the
following cars : +33
My PHP script that receive the posted data ($_POST variables)
don't receive the '+' cars.
Why is that ??
Here is my save function :
function saveEditorContent() {
var dataString = "elm1="+
tinyMCE.getInstanceById('elm1').getHTML()
+ "&contentfilename=" +
document.getElementById('contentfilename').value
var options = {
postData: dataString,
headers: {"Content-Type":
"application/x-www-form-urlencoded; charset=ISO-8859-1" },
errorCallback: saveEditorError
var url = "editorAction.php?action=savecontenteditor";
var req = Spry.Utils.loadURL( "POST", url, true,
saveEditorSucess,options );
removeMCE('elm1');
spEditor.showPanel( 2 ) ;
function saveEditorSucess(req) {
Spry.Utils.setInnerHTML('statusMessageBox',
'SUCCESS<br/>'+req.xhRequest.responseText );
Thank you
CorentinHello,
You miss a step in this code. You'll have to URI encode every
value you will send to the browser. In your situation the + in the
phone has a special meaning in the URI encoding and will be treated
as a blank space.
Please modify the following line:
var dataString = "elm1="+
tinyMCE.getInstanceById('elm1').getHTML()
+ "&contentfilename=" +
document.getElementById('contentfilename').value
with this one:
var dataString = "elm1="+
encodeURIComponent(tinyMCE.getInstanceById('elm1').getHTML())
+ "&contentfilename=" +
encodeURIComponent(document.getElementById('contentfilename').value);
Regards,
Cristian -
Spry.Utils.loadURL problem
I'm having trouble with the last parameter of
Spry.Utils.loadURL.
I know I need more info in the last argument but I cannot
find good documentation on this. I can work out how to do it with
form data, but not with data passed as a simple variable parameter.
ANY help appreciated!
In the code snippet below I am trying to pass a value to
Spry.Utils.loadURL that will them be passed to resfunc as request.
But I cannot work out how to do this.
The function that calls Spry.Utils.loadURL works ok., I test
that it is receiving the correct value by using alert(value). But
how do I get this value into the argument list of
Spry.Utils.loadURL so I can display it?
function doFormPost(url,photoId,resfunc) {
formData = encodeURI(photoId);
Spry.Utils.loadURL('POST', url, true, resfunc,
{"Content-Type": "application/x-www-form-urlencoded;
charset=UTF-8"});
function resFunc(request) {
var mydiv = document.getElementById("foo");
var result = request.xhRequest.responseText;
$("result").innerHTML = "Result was: " + result ;
mydiv.innerHTML = result;
function HandleThumbnailClick(id, photoId)
StopSlideShow();
doFormPost('moon.cfm', photoId,resFunc);
dsPhotos.setCurrentRow(id);
ShowCurrentImage();Hi Kate,
I think what you are trying to do is this:
function doFormPost(url,photoId,resfunc) {
// URL encode the photoId value.
formData = encodeURI(photoId);
// Build the headers object *outside* of the loadURL call to
reduce *visual* confusion.
// All we want to do here is to make sure that when we make
the request, that the browser also
// tell the server that the data we are sending in postData
is url encoded.
headers = {};
headers['Content-Type'] = "application/x-www-form-urlencoded;
charset=UTF-8";
// Send the post request, making sure to pass the content
type header and formData in the options object.
Spry.Utils.loadURL('POST', url, true, resfunc, { headers :
headers, postData: formData });
The 5th argument loadURL is an options object. You can
specify the named properties on this object that you want, for
example "headers" and "postData". The names of the option
properties you can specify are:
username - String that specifies the username on the server
to use when making the request.
password - String that specifies the password to use when
making the request.
postData - URL encoded string of name value pairs. Used when
the request is made with "POST".
errorCallback - Function to call if an error comes back from
the server after the request is made.
headers - Object that allows the caller to send additional
HTTP headers with the request. The properties on this object are
the named after the HTTP property. The value for the property is
the value to send. For example if you wanted to send this HTTP
property as part of the request header:
Content-Type: application/x-www-form-urlencoded;
charset=UTF-8
You would add this to your options object:
headers: { "Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8 }
userData - This is anything you want to pass into loadURL. It
will be stored on the request object so that when your
successCallback/errorCallback is triggered, you can access it. For
example:
var myString = "This is a test!";
function myCallback(request)
alert(request.userData); // Alerts with the string "This is
a test!"
Spry.Utils.loadURL("POST", url, true, myCallback, { headers:
headers, postData: formData, userData: myString });
I realize this is annoying that it isn't documented
extensively. We are working on it.
--== Kin ==-- -
Spry.Utils.loadURL userData
is it possible (and how) to get the userData property back to
my success-script?
Spry.Utils.loadURL("GET",
"/updater.cfm?"+formfield+"="+encodeURIComponent(fieldcontent),
false, checkstatus, {userData: "4711"});
function checkstatus(request) {
var fieldcontent = request.xhRequest.responseText;
var userdata = ?????
herbertHi Kate,
I'm guessing this is related to your validation function
post, so I'm going to give an example with that assumption:
function MyValidationFunction(value, options)
// Call loadURL() to validate the username synchronously.
Notice
// that I passed a false for the 3rd arg to force the
request to be
// processed synchronously.
var request = Spry.Utils.loadURL("POST",
"userExists.cfm?name=" + encodeURIComponent(value), false);
// The assumption here is that the server is going to reply
// with the word "true" (Content-Type: text/plain) if the
name
// already exists, or "false" if it doesn't already exist.
If
// you want to get fancy and return a response wrapped in
XML,
// or some other format, you'll have to parse it out
yourself.
// Since this validation function needs to return "true" if
the
// name doesn't exist, we need to make sure we don't get a
"true" value back!
return request.xhRequest.responseText != "true";
--== Kin ==-- -
Spry.Utils.loadURL - IE7
I built a pre-loader that uses loadURL to complete 35 tasks.
After a task completes it updates the screen asynchronously with
the % completed. It works in Firefox every time, but always fails
in IE7 at 84%. In other words, after a specific amount of time IE7
seems to bail. When IE7 bails all unfinished tasks trigger the
error callback, but req.xhRequest.statusText is empty.
For the sake of debugging all tasks are the same. If I reduce
the number of tasks it will complete in IE7.
So it seems quite clear there are no specific code issues.
The debugger shows no errors. Basically, in IE7 after a certain
amount of time the connections are just lost.
I have such a huge time investment in this software that this
is a huge nightmare. I didn't notice the problem until after
everything was done.
Does any know if there is a client side timeout in IE7? Its
seems that is what is happening. I would appreciate to hear anyones
ideas.This is what my test case looks like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=utf-8" />
<title>LoadURL Test 01</title>
<link href="../samples.css" rel="stylesheet"
type="text/css" /><br />
<script type="text/javascript"
src="../../Spry/includes/xpath.js"></script>
<script type="text/javascript"
src="../../Spry/includes/SpryData.js"></script>
<script type="text/javascript">
var dsStates = new
Spry.Data.XMLDataSet("../../Spry/data/states/states.xml",
"/states/state");
function DoTest()
var context = new Object;
context.data = dsStates.getData();
context.index = -1;
LoadNextURL(context);
var gLock = 0;
function LoadNextURL(context)
var rowIndex = ++context.index;
var count = context.data.length;
var row = context.data[ rowIndex ];
if (rowIndex >= count)
Spry.$("status").innerHTML = "Done";
return;
var url = "../../Spry/data/states/" + row.url;
Spry.$("status").innerHTML = "Loading (" + (rowIndex + 1) +
" of " + count +") " + url + " ...";
if (gLock)
Spry.Debug.trace("WARNING: Re-entering lock! " + gLock);
++gLock;
if (Spry.$("forceAsync").checked)
setTimeout(function() { Spry.Utils.loadURL("GET", url, true,
LoadCallback, { userData: context, errorCallback: LoadErrorCallback
}); }, 0);
else
Spry.Utils.loadURL("GET", url, true, LoadCallback, {
userData: context, errorCallback: LoadErrorCallback });
--gLock;
function UpdateProgressBar(percent)
var ele = Spry.$("progressBar");
var width = ele.offsetWidth * percent;
ele.getElementsByTagName("*")[ 0 ].style.width = percent +
function LoadCallback(req)
var context = req.userData;
var currentIndex = context.index;
var count = context.data.length;
UpdateProgressBar(((currentIndex+1) / count) * 100);
LoadNextURL(context);
function LoadErrorCallback(req)
var context = req.userData;
Spry.$("errors").innerHTML += "Failed to load " +
context.data[ context.index ].url + "<br />";
LoadNextURL(context);
</script>
<style type="text/css">
.ProgressBar {
width: 200px;
height: 16px;
border: solid 1px black;
padding: 0px;
position: relative;
margin: 4px;
overflow: hidden;
.ProgressBarStatus {
position: absolute;
height: 16px;
width: 0px;
background-color: #3399FF;
</style>
</head>
<body>
<div spry:region="dsStates">
<p spry:state="loading">Loading states.xml file
...</p>
<div spry:state="ready">
<p>Total of {ds_RowCount} rows loaded. <input
type="button" value="Start Test" onclick="DoTest();" />
<label>Force Async Requests: <input type="checkbox"
id="forceAsync" /></label></p>
<div id="progressBar" class="ProgressBar"><div
class="ProgressBarStatus"></div></div>
<div id="status"></div>
</div>
<div>
<p id="errors"></p>
</div>
</div>
</body>
</html>
--== Kin ==-- -
Spry.Utils.loadURL problem with IE7
Hello there. First of all sorry for my english.
I have a problem with Spry.Utils.loadURL function in Internet
Explorer 7 only. In firefox works fine.
So i have an asp file that returns a string with some stuff.
I load this asp file with Spry.Utils.loadURL like this.
function ReLoadExtras() {
var req = Spry.Utils.loadURL("GET",
"ControlExtras.asp?type=get&id=50", true, MySuccessCallback);
function MySuccessCallback(req) {
Spry.Utils.setInnerHTML('Extralist',
req.xhRequest.responseText);
In interner explorer works fine only at the first page load.
I have a button that fires the ReLoadExtras() function. But if i
hit it again to reload the div's content return nothing or returns
the same string. The string that ASP file returns is a value from
database but if i change this value on the database and hit the
button again i get the previus value. I think that something is
going wrong with the internet explorer's cache. I'm going crazy
because in firefox works without any problem.
Anynone who can help :-)Finally i fixed this problem. The problem was Internet
Explorer's cache. Every time that i call the asp file i have to
make the url unique. So i create a function that creates a unique
url.
function ReLoadExtras() {
var url =
"ControlExtras.asp?ControlExtras.asp?type=get&id=50";
var req = Spry.Utils.loadURL("GET", MakeUniqueQuery(url),
true, MySuccessCallback);
function MySuccessCallback(req) {
Spry.Utils.setInnerHTML('Extralist',
req.xhRequest.responseText);
function MakeUniqueQuery(x) {
var temp = Math.random() * 3;
var tempurl = x + "&sid=" + temp;
return tempurl;
} -
Spry.Utils.LoadURL Help
I've set up a form:
<form>
<input id="question" type="hidden">
<input id="answer" type="text" onblur="checkAnswer();">
<span id="response"></span>
<input id="submit" type="button" value="submit">
</form>
using spry.utils.loadurl successfully pass the question and
answer to a php page that verifies that the question and the answer
are found in the database... it then returns a "Valid answer" or
"invalid answer" to the span below the form.
it is successfully returning the correct response...but it
doesn't prevent the user from submitting the form...any
ideas?I've set up a form:
<form>
<input id="question" type="hidden">
<input id="answer" type="text" onblur="checkAnswer();">
<span id="response"></span>
<input id="submit" type="button" value="submit">
</form>
using spry.utils.loadurl successfully pass the question and
answer to a php page that verifies that the question and the answer
are found in the database... it then returns a "Valid answer" or
"invalid answer" to the span below the form.
it is successfully returning the correct response...but it
doesn't prevent the user from submitting the form...any
ideas? -
Spry.Utils.loadURL Check Inet Connection
We have a customer that uses a flakie internet connection
(GPRS Connection). I'm trying to use spry to see if the connection
has dropped, then display a DIV tag in their current webpage. I
think you can do it with the "errorCallback" but I can't seem to
get it to work.
Any suggestions, or should I use a different function?
quote:
---- PseudoCode ----
<!-- Loop the below code every 30 seconds to check for a
connection -->
function MySuccessCallback(req)
item.style.display = "none";
function MyErrorCallback(req)
// Display error message on screen.
item.style.display = " ";
var req = Spry.Utils.loadURL("GET", "
http://myserver/mypage.cfm",
true, MySuccessCallback, { errorCallback: MyErrorCallback });This worked for me in IE, but throws an error in Firefox when
the internet connection fails.
Firefox Error:
quote:
[Exception... "Component returned failure code: 0x80040111
(NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.status]" nsresult:
"0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame ::
http://myserver/myfolder/1.3/includes/SpryData.js
:: anonymous :: line 123" data: no]
http://myserver/myfolder/1.3/includes/SpryData.js
Line 123
Code works in IE:
quote:
function doLoad(){
setTimeout( "autorefresh()",
<cfoutput>#session.metarefresh#</cfoutput>*1000 );
function autorefresh(){
var ts = new Date();
var pageLoad = 'menu.cfm?' + ts;
var req = Spry.Utils.loadURL("GET", pageLoad, true,
MySuccessCallback, { errorCallback: MyErrorCallback });
doLoad();
function MySuccessCallback(req){
ID = document.getElementById('iConnection');
ID.style.display = "none";
function MyErrorCallback(req){
ID = document.getElementById('iConnection');
ID.style.display = "";
<span id="iConnection" style="display:none;">Internet
Connection Down</span> -
Spry.Utils.loadURL(... failure
This code works properly in local:
var request_URL
="/petitions/client/remote/authenticate.cfm?username="+uName+"&password="+uPass;
Spry.Utils.loadURL("GET", request_URL, false, authBack);
Once I upload, I add "
http://wconti.com... :
var request_URL ="
http://wconti.com/petitions/client/remote/authenticate.cfm?username="+uName+"&password="+u Pass;
Spry.Utils.loadURL("GET", request_URL, false, authBack);
I get the following error:
Exception caught while loading
http://wconti.com/petitions/client/remote/authenticate.cfm?username=contiw&password=italia :
The download of the specified resource has failed.
Just starting with Spry, please bear with me.
Thank YouThis code works properly in local:
var request_URL
="/petitions/client/remote/authenticate.cfm?username="+uName+"&password="+uPass;
Spry.Utils.loadURL("GET", request_URL, false, authBack);
Once I upload, I add "
http://wconti.com... :
var request_URL ="
http://wconti.com/petitions/client/remote/authenticate.cfm?username="+uName+"&password="+u Pass;
Spry.Utils.loadURL("GET", request_URL, false, authBack);
I get the following error:
Exception caught while loading
http://wconti.com/petitions/client/remote/authenticate.cfm?username=contiw&password=italia :
The download of the specified resource has failed.
Just starting with Spry, please bear with me.
Thank You -
Spry.Utils.loadURL and https
Hi,
I'm using Spry's loadURL to send and recieve a response from
a PHP script. using a "relative" url path.
when the page is loaded in https Spry give me a
quote:
Error: uncaught exception: Security Error: Content at
https:// may not load data from '
http://'
if the page was loaded via https why does Spry loadURL from
http when the url path is set to relative?the php script simply "prints $response;" if ajax requests
were in https is it safe to assume that the print response would be
https as well? -
Requesting new methods for Spry.Utils. Notifier
I'm building a quiz application with Spry that is based on a
frameset. The main DataSet is contained in the frameset page, and
it swaps in different question type templates in a child frame.
Each of these templates creates NestedXMLDataSets and has Spry
Regions.
When re-using the same template twice in a row, IE will throw
a freed script error, because the objects held by the observer in
the main frame are still in memory, but the page they came from is
gone. (Firefox will work if you remove the Nested Data Set from the
observer list, apparently replacing a spry region with itself is
ok. I'm assuming since the new region is identical to the old one,
the newly loaded page occupies the same space in memory as the old
one, so everything just works as if it were 'new'.)
The solution, therefore, is to remove any observers from the
DataSet in the main frame, that live in the templates in the child
frames.
However, since the region is assigned as an observer
automatically, I have no way of knowing which position it occupies
in the observers array, or how to reference it. So, in order to
remove it, I have to access parent.myDS.observers directly, and
remove all observers but the first. (Knowing that the first
observer on the list is one I manually added and need to persist.)
Bascially, what we need is a method to ID and remove regions
from the observer list, in those cases where a region (and page
containing it) are going to be destroyed but the data set it
populates from persists.
e.g. <spry:regionid="foo">
Also, since there are other objects like Nested Data Sets
that automatically get added to the observer list, it would be nice
to be able to inspect the list.
e.g.: myDataSet.getObservers()
Sorry if this doesn't make much sense, I'm on a lack of sleep
and burned out from debugging at the moment. I'd love to help the
Spry team continue to refine the product because I think it's
great, and in practice, for the most part, it's made developing
RIA's such as e-Learning applications that have to rely on static
XML much much easier.PaulColombo wrote:
> Bascially, what we need is a method to ID and remove
regions from the observer
> list, in those cases where a region (and page containing
it) are going to be
> destroyed but the data set it populates from persists.
> e.g. <spry:regionid="foo">
You don't need to know the index in the observers array to be
able to remove one, you do how ever need to have a reference to the
object doing the observing to be able to remove it. Each data set
inherits the methods of the Notifier object, so your dataset has a
removeObserver method, so pass in the object doing the observation,
like so:
myDataSet.removeObserver(observerObj);
I've not delved too much into the datasets, so with auto
added observers, not quite sure what the observer is that needs to
get removed, but if you can figure it out you can use
removeObserver();
> Also, since there are other objects like Nested Data
Sets that automatically
> get added to the observer list, it would be nice to be
able to inspect the list.
>
> e.g.: myDataSet.getObservers()
You already have direct access to the observers array with
myDataSet.observers, what do you get by having a method rather
direct access? Regardless, you can add that method yourself to the
Notifier object and it'll get automatically added to your datasets:
Spry.Utils.Notifier.prototype.getObservers =
function(observer)
return this.observers;
Danilo Celic
|
http://blog.extensioneering.com/
| WebAssist Extensioneer
| Adobe Community Expert -
Cache of Spry.Utils.updateContent data?
So I have a need for a Tab system that pulls data from other
files to load into the tabs. Any links in those files will trigger
the file to load into the tab (think like an iframe, but no
iframes). If the link is to an external site it will work as
normal. The links in my outside files don't need any js, id or
class in them. Just normal html. With some help from the yahoo
event and dom library I was able to detect clicks and redirect
everything very nicely. Did I need to use YUI to do the event
listener? Not sure, it made it pretty easy and I am no JS expert. I
didn't see anything within spry to help in that regard.
Example Here:
http://dev.besavvy.com/Spry_P1_4_1214/samples/tabbedpanels/tabbed_panel_sample.htm
My trouble comes with caching of the files. It seems FF
caches my .html files but not .cfm ones. IE caches both. I can use
the cfheader tags to prevent the cache I am sure, but that doesn't
fix .html files. I know I can work around it but I wanted to check
here as I am not clear if there is an optional attribute or another
direction I should take. It would be very handy to have a :cache
true :cache false type of solution.Try:
Spry.Utils.updateContent('apDiv1',elTarget,null,{method:'POST'});
which I believe will cause it to use the browsers post
mechanism which never caches.
Or:
var ts = new Date();
Spry.Utils.updateContent('apDiv1',elTarget+'&TS='+ts.toString());
which will add a unique date string to the end of the url
being requested making the browser believe it is a new page
Oh if the page doesn't already have a query string change +
'&TS=' to + '?TS='
Regards,
Chris Phillips
Senior Application Developer
www.dealerpeak.com -
Spry.Utils.updateContent & IE caching pls help
Hi -
so ive read quite a few posts about this problem but no
single - comprehensive solution. Basically im using updateContent
to show and refresh a mini basket display on my ecom site. works
great in firefox but in IE7 the shopping basket apparently gets
cached the first time it gets updated and and then only updates the
cached version thus making it appear as if you havent actually
added anything to your cart. I know it's a cache issue cause I
clear the cache, re-updateContent, and the most current basket
state renders.
It seems like this should be documented somewhere - hopefully
someone can offer up a straightforward solution.
Cheers
AndyHi Andy,
By default Spry.Utils.updateContent() uses the "GET" method
for requesting a fragment from the server. What this means is that
you are at the mercy of the Browser's caching system and the
caching rules specified for that fragment by the Web Server. IE6,
not sure about IE7, also had a bug where even though the server
says don't cache my page, IE still insists on caching the page ...
a workaround was to append a query param to your URL that made the
URL unique, for example the local time:
Spry.Utils.updateContent(ele, "myfoo.php?id=12&curTime="
+ Date.now(), myCallback);
The other workaround is to use the "POST" method. Most
browsers don't cache results from a "POST" because that is what
most web applications use to fetch data that changes.
Spry.Utils.updateContent(ele, "myfoo.php?id=12", myCallback,
{ method: "POST" });
--== Kin ==-- -
Spry.Utils.updateContent()
I wonder if there is a way to evaluate the scripts contained
in a request using updateContent()?. I've been looking for the
answer with no success, can any one point me to the answer or tell
me if it can be done?
Cheers, Ivan.Forget this post, i've seen the change log for Spry
PreRelease 1.4 and since then Spry.Utils.updateContent(), dependant
of Spry.Utils.setInnerHTML(), evaluates any script.
Thanks anyway -
Spry.Utils.stringToXMLDoc or a Substitute
I am looking for a detailed guide for using the
Spry.Utils.stringToXMLDoc function. I am not having the best of
luck on finding detailed information on the function and would like
to learn more about. I would appreciate it if you could please post
some resources on this function or if there are other ways to get a
xml string into a XMLDataSet.
CheersWe only have this:
http://labs.adobe.com/technologies/spry/articles/data_api/apis/utils.html#stringtoxmldoc
and this:
http://labs.adobe.com/technologies/spry/samples/data_region/XMLStringSample.html
--== Kin ==--
Maybe you are looking for
-
IDOC not displayed in billing document
Hi Experts, I have a issue about the outbound idoc's generated from two different billing document. I can see the idoc's from these documents though the path Goto>header>Output ---> Processing log. But if i check the services of object button for one
-
So I have pages documents and numbers documents that were created on my iPhone5s. When I open them on my iPad (1st gen) it says I need to upgrade to open it, however I can't update the app as it doesn't support it. Is there any way that I can open th
-
Norton password manager does not work on 8. How do I return to a previous version?
when I updated to firefox 8, Norton password manager does not work. How can I return to a previous version. I am surprised that you introduced an update missing this.
-
Saving excel & word docs onto ipad
i have downloaded smart office 2 onto my ipad about 2 months ago, for some reason i can no longer save excel & word documents onto my ipad anymore. before i would hold finger on attchment and it would give me option to open document with smart office
-
HI Experts, I have 4+ years of experience in SAP BI, now I am planning to learn SAP APO. Please give me sugessions is it is useful to future carrier and what are the best materials or documents to learn. Regards, SVS