In the project we have a config singleton object, which holds configuration fetched from the server (ServerConfig). It is defined like
Ext.define('SI.ServerConfig', {
singleton: true,
constructor: function () {
this.callParent(arguments);
// Closure which holds the serverConfig after the synchronus request
var serverConfig;
Ext.Ajax.request({
url: 'example.de/getConfig.php',
method: 'POST',
async: false,
success: function(response){
var responseObj = Ext.JSON.decode(response.responseText);
serverConfig = responseObj.serverConfig;
}
});
//definition of setter/getter function for the serverConfig closure
this.get = function (optionName, defaultVal) {
if (Ext.isDefined(serverConfig[optionName])){
return serverConfig[optionName];
}
return defaultVal;
};
this.set = function (optionName, value) {
serverConfig[optionName] = value;
};
}
});
In the constructor we have a closure which holds after the synchrone Ajax request the server config object.
We need to make a synchrone request, because the server config values are needed in various other classes to provide config bevore creation.
With a setter and a getter function we provide access to the values defined in it.In every controller/view/model we need access to the server config, we require the singleton.
Ext.define('SI.view.TestView', {
extends: 'Ext.panel.Panel',
requires: ['SI.ServerConfig'],
// This fails most of the time in Firefox, but works every time in Chrome and IE 8+
title: SI.ServerConfig.get('testTitle')
});
But when we access the singleton in the config object in class definition, the server config singleton is not instantiated in Firefox all the time. In Chrome and in Internet Explorer 8+ it is working as expected.
So to be shure we have the singleton ready to use we tried the following. We moved the Application definition in the callback of an Ext.require. But this does not fix it for Firefox.
Ext.require([
'SI.ServerConfig'
], function () {
Ext.define('SI.Application', {
// ....
}); /
});
In the Firefox Debugger the following is logged:
Synchrone XMLHttpRequests am Haupt-Thread sollte nicht mehr verwendet werden,
weil es nachteilige Effekte für das Erlebnis der Endbenutzer hat.
Für weitere Hilfe siehe http://xhr.spec.whatwg.org/
From the XHR spezification:
Synchronous XMLHttpRequest outside of workers is in the process of being
removed from the web platform as it has detrimental effects to the end
user's experience. (This is a long process that takes many years.)
Developers must not pass false for the async argument when the JavaScript
global environment is a document environment. User agents are strongly
encouraged to warn about such usage in developer tools and may experiment with
throwing anInvalidAccessError exception when it occurs.
So synchrone requests will be removed in the future and only allowed in webworkers.
We need a solution for this.
The problem only occurs in developer mode, when we build it with sencha app build, it works in Firefox...
Thanks for any suggestions.
And-y
Update index.html -> index.php
I changed index.html into index.php like #colinramsay suggested and included the server config object before microloader is included.
Now the warning about Synchrone XMLHttpRequests is gone in Firefox.
But the problem when accessing the singleton in the config object in class definition still remains for the Firefox.
Another totally different approach is to change your application's root index.html to an index.php that does something like the following:
<!DOCTYPE HTML>
<html manifest="">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var SI = {};
<?php
echo 'SI.ServerConfig = { "testTitle": "some string" }';
?>
</script>
<!-- The line below must be kept intact for Sencha Cmd to build your application -->
<script id="microloader" type="text/javascript" src="bootstrap.js"></script>
</head>
<body></body>
</html>
This allows you to change your get code to something like:
function get(optionName, defaultVal) {
if (Ext.isDefined(SI.ServerConfig[optionName])){
return SI.ServerConfig[optionName];
}
return defaultVal;
}
You are using PHP to directly output your configuration to the HTML page as JSON before Ext JS and your application load.
Try this:
Ext.define('SI.view.TestView', {
extends: 'Ext.panel.Panel',
requires: ['SI.ServerConfig'],
constructor: function() {
this.callParent();
this.setTitle(SI.ServerConfig.get('testTitle'));
}
});
I suspect this is a load order issue. In your original code, Ext.define would run at the same time SI.ServerConfig, before the requires kicks in, so SI.ServerConfig might not have loaded via requires. By calling it in the constructor you can be sure that all of the requires have been fulfilled and so it should be available.
Related
I'm just blundering into AJAX, and I swear I'll actually learn my way around it real soon now, but all I need at the moment is to get the inner browser height, so I can ask the Google Maps Engine for a map of the appropriate height. (I'm actually making that request via the Google Maps plugin under Joomla. If I could somehow make that request on the client side, then I might not really have to mess with AJAX, but this would still be a good introductory exercize for me.)
I'm trying to grasp the basic AJAX setup by putting together a working minimal document incorporating code from the first answer at how to get screen width through php?. I realize that example is asking for a different attribute than I'll be asking for, but I'm just keeping the code as close to the original as possible.
I'm not totally clear on what code goes where, but apparently it is not this, at http://allbluesdance.com/testajax.php :
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Test Ajax</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script>
var width = $(window).width();
//This is ajax code which will send width to your php page in the screenWidth variable
$.ajax({
url: "http://allbluesdance.com/testajax.php", //this will be your php page
data : {screenwidth:width}
}).done(function() {
$(this).addClass("done");
});
</script>
(Yeah, it's running)
<!-- example.php code is here : -->
<?php
echo $width = $_REQUEST['screenwidth'];
?>
</body>
</html>
So unless I've made a dumb syntax error I could use a clue.
Thanks,
Drew
I think you need to specify how AJAX gets the data, either through GET or POST. I Have been using this snippet and it works for me.
// fire off the request
var request = $.ajax({
url: "/list/server.php",
type: "POST",
data: send_data
});
// callback handler that will be called on success
request.done(function (response){
// log a message to the console
console.log(response);
});
I want to get last ajax call made in my code .
here is my code
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="Scripts/jquery-1.7.1.min.js"></script>
<script>
function getCreateAccount() {
$.ajax({
type: "GET",
url: "/Account/Register/",
contentType: "application/json; charset=utf-8",
dataType: "json"
});
console.log($.ajax.mostRecentCall.args[0]);
}
</script>
</head>
<body>
</body>
</html>
but when i see in my console it says "TypeError: $.ajax.mostRecentCall is undefined" .
Thanks,
You may register a global ajaxComplete handler that will be invoked every time an AJAX call finishes.
With this, you can emulate something like the Jasmine $.ajax.calls.mostRecentCall() property:
$(document).ajaxComplete(function(ev, jqXHR, settings) {
$.ajax.mostRecentCall = jqXHR;
});
In this case I'm saving the jqXHR object, rather than the exact set of parameters that was passed to $.ajax.
Note, of course, that this won't be populated immediately after $.ajax is called - it won't be filled until at least one call has finished.
I think mostRecentCall function is from Jasmine framework. You must include Jasmine in your code.
mostRecentCall does not exist in jquery!
there is no such this like "$.ajax.mostRecentCall" in ajax jquery.
As PSR mentioned you could try to use a flag variable
Adding an alert before logging will give enough time for the ajax call to be finished.(that should do the trick)
if you don't want any alerts then you can use sleep() method
There is nothing like
$.ajax.mostRecentCall
in Jquery. That is from Jasmine.js. Unless and until you add reference to Jasmine.js and follow the steps to spy on function, it will give you an error.
You may want to refer the documentation of Jasmin.js. Here's the link that shows how to use it.
I would like to make an ajax call from javascript code (to check if the user session is still active). I would like my code to be independent of any portlet, and hence would like to build the url in javascript, without having to rely on a portlet id.
Is this possible within the MVCPortlet framework ? If not, are there alternatives ?
Edit below this line.
I am aware of liferay's mechanisms for session management, and I actually have set an automatic extension of the session. But two of my users have lost thier long input because the session expired despite this mechanism (I believe they interrupted their work, and their computer went to sleep...).
I would like to avoid such a situation by checking if a session is still active before I submit an input from certain forms. The way I do it is prepend the submit with an ajax call which I detail below.
My true question is : how do I remove the reliance on portletId in that code so that I don't need to change every portlet in which I want to implement this mechanism.
I do it today with the following javascript function:
Liferay.provide(window, "bb_checkSession",
function (sessionId, portletId, successCallback, failureCallback) {
var A = AUI();
var resourceUrl = Liferay.PortletURL.createResourceURL();
resourceUrl.setResourceId("CHECK_SESSION");
resourceUrl.setPortletId(portletId);
A.io.request(resourceUrl.toString(), {
method: 'GET',
cache: false,
on: {
success: function () {
var responseData = A.JSON.parse(this.get('responseData'));
if (responseData == null || sessionId != responseData.sessionId) {
if (failureCallback) failureCallback();
} else {
if (successCallback) successCallback();
}
},
failure: function () {
if (failureCallback) failureCallback();
}
}
});
},
['aui-io', 'liferay-portlet-url']
);
and in my portlet, I handle the call as follows
public void checkSession(ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws IOException {
PortletSession session = resourceRequest.getPortletSession();
JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
resourceResponse.setContentType("text/javascript");
jsonObject.put("sessionId", session.getId());
resourceResponse.getWriter().write(jsonObject.toString());
}
This can be easily achieved through Jquery. If in your web.xml you have defined session-timeout as 15 mins, then at 14th minute a lightbox will appear which will ask User if he wishes to continue the session or logout.
If User wishes to continue session, then /c/portal/extend_session is called(Ajax) else /c/portal/logout is called.
Below code works independent of any type of portlet. You need to place this code in your JSP/Facelets page.
<script src="/static-files/scripts/jquery-1.6.2.js" type="text/javascript" charset="utf-8"></script>
<script src="/static-files/scripts/jquery-ui.min.js" type="text/javascript" charset="utf-8"></script>
<script src="/static-files/scripts/jquery.idletimer.js" type="text/javascript" charset="utf-8"></script>
<script src="/static-files/scripts/jquery.idletimeout.js" type="text/javascript" charset="utf-8"></script>
<link href="/static-files/css/jquery-ui-rev.css" type="text/css" rel="stylesheet"/>
<script type="text/javascript" charset="utf-8">
var $j=jQuery.noConflict();
$j(document).ready(function()
{
$j("#dialog").attr("style","visibility:visible");
var titletext=$j(this).attr('title');
$j("#dialog").bind("contextmenu",function(event)
{ return false; })
// setup the dialog
$j("#dialog").dialog({
autoOpen: false,
modal: true,
width: 400,
height: 200,
closeOnEscape: false,
draggable: false,
resizable: false,
buttons: {
'Yes, Continue': function()
{
$j.ajax({
url: '/c/portal/extend_session',
cache: false,
success: function() {
$j('div#dialog').dialog('close');
}
});
$j(document).attr('title',titletext);
},
'No, Log out': function(){
$j.idleTimeout.options.onTimeout.call($j.post('/c/portal/logout',function()
{}))
}
}
});
// cache a reference to the countdown element so we don't have to query the DOM for it on each ping.
var $jcountdown = $j("#dialog-countdown");
// start the idle timer plugint
$j.idleTimeout('#dialog', 'div.ui-dialog-buttonpane button:first', {
idleAfter: 840, // 14 mins
onTimeout: function(){
window.location.replace("/c/portal/logout");
},
onIdle: function(){
$j(document).attr('title', 'Warning! Due to inactivity, your session is about to expire.');
$j(this).dialog("open");
},
onCountdown: function(counter){
$jcountdown.html(counter); // update the counter
}
});
});
</script>
<!-- dialog window markup -->
<div id="dialog" title="Your session is about to expire!" style="visibility:hidden">
<p>
<span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 50px 0;"></span>
You will be logged out in <span id="dialog-countdown" style="font-weight:bold;whitespace:nowrap"></span> seconds.
</p>
<p>Do you wish to continue your session?</p>
</div>
Vikas V gave you some code that you can use - there are two aspects of your question that don't really match my perception...
Liferay already has such a mechanism - e.g. log in to your portal and let it sit for 30 minutes (or configure the timeout to be earlier) and you'll see that you get a warning message, followed by a "you have been logged out" message if you don't click "extend session". in 6.1 this is even working through multiple open tabs (I believe it's using js-cookies to contain a timestamp, so that all tabs timeout at the same time)
if you make a request to the server in order to check if the session is still active, you risk extending the session - e.g. just checking for the session being alive might extend it eternally
Thus, my recommendation is to either hook into Liferay's mechanism (edit your question or comment here if you need hints where to find it, I'd have to look it up) or to use your own fully client side implementation. If you opt for your own implementation (e.g. like Vikas V suggested) , keep in mind that you might have multiple tabs open and you wouldn't want to be logged out by a stale tab that you forgot about in the background.
Here is my html.
<html>
<head>
<script src="closure-library/closure/goog/base.js"></script>
<script src="hello.js"></script>
</head>
<body onload="sayHi()">
</body>
</html>
And here is my hello.js file.
var options = {
"style" : "background:#EEE;"
}
function sayHi() {
goog.require("goog.dom");
var header = goog.dom.createDom("h1", options, "hello world");
goog.dom.appendChild(document.body, header);
}
Why does Chrome Console issue this error?
Uncaught TypeError: Cannot call method 'createDom' of undefined
In general, I don't think I can require a library in the same code block in which I call a function from the library. I just saw this in the documentation, but I was wondering why.
In uncompiled Closure code, each goog.require(NAMESPACE) results in a call to:
document.write('<script src="SCRIPT_PROVIDING_NAMESPACE"></script>');
The dependencies are defined with calls to goog.addDependency(relativePath, provides, requires) in deps files generated with depswriter.py. Using the Chrome Developer Tools, the Elements tab shows all of the <script> elements dynamically inserted based on the Closure Library dependencies defined in deps.js.
In your example, when sayHi() is triggered by the body onload event, a new <script> element is inserted in the header. However, this new <script> element has not yet loaded goog.dom. Therefore, goog.dom is undefined.
If you change hello.js as follows:
goog.require("goog.dom");
var options = {
"style" : "background:#EEE;"
}
function sayHi() {
var header = goog.dom.createDom("h1", options, "hello world");
goog.dom.appendChild(document.body, header);
}
Then goog.require("goog.dom") gets executed prior to the onload event and the inserted <script> element has a chance to load dom.js prior to the call to sayHi().
So I have my first MVC2 site that I'm working on and naturally I'd like to throw some AJAX in there. The problem is, is that I don't know how to get the URL for the action when passing in a URL parameter. Let me explain. The examples I've seen so far show the developer passing in strings like '/MyController/MyAction'. That's great, except if your controllers are not in the root directory of your website (as is the case in my situation). I could always use relative URLs like 'MyAction' except if the URL contains parameters that doesn't work either. Consider http://example.com/myroot/MyController/MyAction vs http://example.com/myroot/MyController/MyAction/PageNumber/SomeOtherValue. Now the relative URL will be incorrect.
In the ASPX code, this is easy. I just write in <%= Url.Action("MyAction") %>. But how do I do this in my javascript file?
This is part of the long-standing issue that including server-sided code in JavaScript files is not really possible :(. (Without serious hacks, that is.)
The best solution is to include the action URL inside your HTML file somewhere, then get that value from JavaScript. My suggestion would be something like this:
<!-- in your view file -->
<form id="MyForm" action="<%: Url.Action("MyAction") %>"> ... </form>
<!-- or -->
<a id="MyLink" href="<%: Url.Action("MyAction") %>"> ... </a>
combined with
// In your .js file
$("#MyForm").submit(function ()
{
$.post($(this).attr("action"), data, function (result) { /* ... */ });
return false;
});
// or
$("#MyLink").click(function ()
{
$.getJSON($(this).attr("href"), data, function (result) { /* ... */ });
return false;
});
This feels semantically clear to me, and in some cases even creates degradable fallback behavior for when JavaScript is turned off.
You can't do this in your JavaScript file directly, however you can pass these dynamic values into your script by way of a script initializer. Consider the following example:
External Js file
ShoppingCart = function() {
this.settings = {
AddProductToCartUrl: '',
RemoveFromCartUrl: '',
EmptyCartUrl: '',
UpdateCartUrl: ''
};
};
ShoppingCart.prototype.init = function(settings) {
this.settings = jQuery.extend(this.settings, settings || {});
};
HTML/View
<script type="text/javascript">
var cart = new ShoppingCart();
cart.init({ AddProductToCartUrl: '<%=Url.Action("MyAction")%>' });
alert(cart.settings.AddProductToCartUrl);
</script>
Simple: tell your javascript what the correct URL is.
Tactically, you can get there alot of ways, but they basically break down into two techniques:
Have a server-side generated javascript "configuration" so you can do something like var url = siteConfiguration.SITEROOT + 'products/pink-bunny-slippers' Note this file can be a normal MVC view, the only trick is you have to tell the controller to send a text/javascript header rather than text/html.
Basically, dependency inject it into your script. IE function wireUpAjaxLinksToService(linkIdentifier, serviceEndpoint) where you call using something like wireUpAjaxLinks('a.ajax', '<%= Url.Action("MyService", "Services") %>')