Best ajax questions in February 2012

What's the best way to use HTTP Authentication in an Ajax Application that's not 100% AJAX

7 votes

I have a standard HTML login page, which I would much rather use than the standard HTTP authentication pop-up provided by browsers. Today, I am using session cookies to keep track of the session after logging in, but I'd like to be stateless and pass the HTTP authentication every time. The web services I am hitting already support this, so this is a browser-only issue.

Adding authentication credentials is trivial in jQuery, but I don't know how to keep them around. If you go from the login page (a jsp) to the Home page (another jsp) you clearly don't keep the username and password fields from the login page. I know some browsers will store your HTTP authentication credentials if you enter them from the pop-up, but I don't know if they get stored when using an XHRRequest. If they do, is there much consistency among browsers?

Also, the user needs to be able to "sign out" of the application, too. If the browser stores the authentication credentials, is there a way to clear them using JavaScript.

I feel like I can't be the first person to try to solve this. Is there some jQuery plugin or something that already handles this? Or is it simply not possible to do what I'm trying to do?

Most answers miss the point, which is to avoid having any server-side session. I don't want any application state in the server. I'll award the bounty to answer that came closest, but the real credit goes to the rest-discuss group and Jon Moore for the correct answer and to Mike Amundsen for helping me to actually understand it.

The best answer I've gotten is to use a cookie, but not the typical automatic session id cookie given to you by most application servers. The cookie (which will automatically be sent with each subsequent request) is a user identifier and time signed by the server. You can include an expiration time with the cookie so it simulates the typical 30 minute session on a server (which means you have to push it forward with subsequent requests) as well as keeps the same cookie from being valid forever.

The XHR/AJAX part is a red herring. This will work whether you are doing XHR requests or an old-fashioned page-by-page web application. The main points are:

  • The cookie is automatically sent on subsequent requests so there's no special scripting required - it's just how browsers work already.
  • The server does not need to store any session for the user, so the user can hit any server in a cluster and not have to re-authenticate.

Javascript: suppress "This page is asking you to confirm that you want to leave" popup on onbeforeunload

7 votes

When a user leaves a page, I need to ask him if he wants to perform a particular action before leaving.

<script type="text/javascript">
$(document).ready(function() {
        window.onbeforeunload = askConfirm;
});

function askConfirm() {
    var addFriend = confirm("Would you like to ping?");
    if (addFriend) {
        var xmlhttp;
        if (window.XMLHttpRequest) {
            xmlhttp=new XMLHttpRequest();
        } else {
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
        xmlhttp.open("GET","http://example.com?ping=1234",true);
        xmlhttp.send();
    }
    return null;
}

Depending on the value of that last return statement ("", null, true, false, or not having a return statement at all) I can have one of two situations:

1) The user gets the "Would you like to ping?" confirmation (good), the ping is sent (good), and the user is presented with a "This page is asking you to confirm that you want to leave" popup (BAD).

-or-

2) The user gets the "Would you like to ping?" confirmation (good), the ping is not sent (BAD), and the user is not presented with a "This page is asking you to confirm that you want to leave" popup (good).

How can I have the AJAX ping sent, yet suppress the "This page is asking you to confirm that you want to leave" popup?

Edit: As ridiculous as it sounds, the "work around" that I found for this issue is to alert() after the xmlhttp.send() statement. The only clean way to do that is to alert the user that his ping has been sent. If future StackOverflowers find a better solution, I would love to know.

Thanks.

well instead of doing it before you leave, I did it onunload and here is what I got to work in FF and IE (besides chrome, that's all I got): http://jsfiddle.net/rmpLm/9/.

perhaps you can get it to work on chrome, but if you cant, maybe you could make it so that if you hit ok, it takes you to a page where you send it. hope it helps : )

EDIT: maybe you should consider making it so it does it onload

'this' reference in JavaScript

5 votes

I have a little problem in object programming in javascript

There is a "class" Task, it has several methods, a method containing an asynchronous sending of a request with a help of JQuery ($.ajax). After request is success it's necessary to perform a particular method (e.g. successFunction) of the class Task.

The problem is, after the query in the body of successFunction it's impossible to refer to the class using the keyword this, because the context has changed, and this contains a reference to the jquery-object which performs an ajax-request.

What variants to refer to the current Task object inside a function that was not caused directly but externally exist? (For example by an event or ajax)

Normally inside an AJAX event such as the success callback, this refers to the object returned by the $.ajax call. You could use the context parameter to change the context in the success callback:

$.ajax({
    url: '/foo',
    context: this, // <!-- change the context of the success callback
    success: function(result) {
        // 'this' here will refer to whatever it refered outside
    } 
});

You could also pass complex objects:

$.ajax({
    url: '/foo',
    context: { element: this, foo: 'bar' },
    success: function(result) {
        // you can use 'this.element' and 'this.foo' here
    } 
});

does manual garbage collection of DOM elements in jquery improve browser performance at all?

5 votes

with scope on performance, does it make any sense to remove elements that are not longer needed? or do browsers perform auto garbage collection on dom elements, that are not further referenced in code?

$('some_element').fadeOut(1000, function(el){
   $(el).remove(); // <-- does this make sense at all?
});

This code:

$('some_element').remove();

tells the browser that you are done with that element and no longer need it in the DOM. If you don't have any other references in your javascript to that element, the garbage collector will then free the memory that it uses.

If you do not remove it, then the DOM element stays in your web page for as long as that web page is displayed. It will never be garbage collected because the browser has no way of knowing whether you intend for it to stay in the page or not.

It is a good practice to manually remove DOM elements that are no longer needed.

But, in 99% of the cases, it will not matter in any way because the memory used by one DOM element is trivial compared to the overall memory used by a web page. Everything in the web page will be freed when the user goes to another web page anyway.

The main time that it does make a significant difference to free something like this is when you are doing the same operation over and over again (in a big loop, on a timer, etc...). In that case, you do not want objects to pile up and consume increasing amounts of memory as the page is used.

How to handle jQuery ajax post error when navigating away from a page

5 votes

Is there a way to handle the error from an ajax request within JQuery such that when the user navigates away from the page, the resulting error is ignored?

$(document).ready(function() {
 $.ajax( {
    url:'/server/url/',
    type: 'POST',
    data: {some..data},
    success:function(response) { do stuff with response },
    error: function(xhr, textStatus, errorThrown) {
           // Don't raise this alert if user has navigated away from the page
           alert('error');
    }
});

I'm curious if anybody knows of a clean way to handle this? I know you could create some logic in a $(window).bind("beforeunload", function(){}) method but I'd prefer if there was a way to handle the situation without doing this since Chrome appears to no longer support it. Is there for example a specific error that is returned in this case?

Thanks.

It looks like the answer to this is to examine the jqXHR.status. The XMLHttpRequest spec outlines these steps to set the status:

The status attribute must return the result of running these steps:

  1. If the state is UNSENT or OPENED, return 0 and terminate these steps.

  2. If the error flag is set, return 0 and terminate these steps.

  3. Return the HTTP status code.

NOTE also: The error flag indicates some type of network error or request abortion. It is initially unset and is used during the DONE state.

From what I understand therefore, this code check should fix the issue:

    if (xhr.status != 0)
        alert('error');

How can I make remember voting with cookies easier than this?

5 votes

It is the most easiest to describe my problem with a working example: even if you are not logged in, YouTube remembers what you have watched, and next time gives you suggestions based on previous watched movies.

My site is similar in a way: the users can vote on articles without logging in, and the site remembers votes with cookies. I have figured out a working method, but there has to be an easier way - also now the DB usage is anything but optimized.

For every visitor there is a check if he has the cookies. If yes I query his votes. If not I create a dummy user, and send him out the cookies. Now I store this users "last_visit" timestamp. After this everything is the same for both users. My problem is that my DB is filling up with dummy users, so I made my cookies expire in 3 months and my site regularly check which users didn't visit my site in the last 3 months, and deletes them from the DB.

I know I overcomplicated this, but my vote system is using AJAX, and I couldn't find a method to send out a cookie (and create the dummy user) only if a vote happens and not every time a simple visitor browses my site - without vote.

Also a note: I insist on using cookies - I know it would be easier to store IP-s when a vote happens, but there are schools, businesses using the same IP, and I like to allow their users to use my site.

What did I miss here? How can this be optimized?

if they do not hold a permanent account, why store anything related to them in the database at all? just record their prior votes in the cookie. you would also store averall votes in the db, but anonymously, and not relate these to "users" at all.

How do I renew a Facebook user_access_token if I deal with a lot of AJAX?

5 votes

Please tell me if I'm understanding correctly. (because I might not be.)

  1. User posts something on my site. (He checked "also post to Facebook".)
  2. Client sends an AJAX POST request to my server, and my server inserts the record in my database.
  3. The server realizes the the facebook user access token is expired, so it sends the response back to the client, while storing the post in a session.
  4. The client does a window.location.replace(facebook_oauth_dialog_url)
  5. Then the user will see a sudden "flash", going to Facebook, then coming back to the website. My server picks up the new access token.
  6. My server checks the session to see what should be posted to Facebook. And then, it uses the new access token to post that to Facebook.

Is it really this tedious? Why can't I renew the app server-side without the user going through the dialog???

My entire site is Backbone.js. That means, it's one big page. I can't jump the user back and forth between Facebook and my website like this.

The idea is to make use of the Facebook JS-SDK methods:

  1. User check the Post To Facebook option
  2. you check if the current user is connected to your app (using FB.getLoginStatus())
  3. if the user is connected, you have two options:
    • post directly using the FB.api method or
    • Send the access_token to your server to complete the post process there
  4. if the user is not connected (or not logged in to Facebook), use the FB.login() method

Here's a quick example (with a Live Demo!) for you to get started:

<!DOCTYPE html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<div id="fb-root"></div>
<script>
var fbLoaded = false;
window.fbAsyncInit = function() {
    FB.init({
      appId      : 'YOUR_APP_ID', // App ID
      //channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File
      status     : true, // check login status
      cookie     : true, // enable cookies to allow the server to access the session
      xfbml      : true  // parse XFBML
    });
    fbLoaded = true;
    // Additional initialization code here

};

function postForm() {
    var msg = document.myForm.msg.value;
    // do form validation here, e.g:
    if(!msg.length) {
        alert("You should enter a message!");
        return false;
    }

    // do we need to post to Facebook?
    if(document.myForm.toFB.checked) {
        // is the library loaded?
        if(!fbLoaded) {
            alert("Facebook JS-SDK is not yet loaded. Please try again later or uncheck Post To Facebook option");
            return false;
        }

        FB.getLoginStatus(function(response) {
            if (response.status === 'connected') {
                var uid = response.authResponse.userID;
                var accessToken = response.authResponse.accessToken;
                /* 
                *  message can be posted to Facebook directly
                *  using the FB.api method or accessToken
                *  can be sent to the server and do the call
                *  from there
                */
                myAjaxCall(msg, accessToken);
            } else {
                // status is either not_authorized or unknown
                FB.login(function(response) {
                    if (response.authResponse) {
                        var accessToken = response.authResponse.accessToken;
                        myAjaxCall(msg, accessToken);
                    } else {
                        alert('User cancelled login or did not fully authorize.');
                    }
                }, {scope: 'publish_stream'});
            }
        });
    } else {
        myAjaxCall(msg);
    }
    return false;
}

function myAjaxCall(m,a) {
    alert("Here you make the ajax call\nMessage: " + m + "\nAccess Token: " + a);
}

  // Load the SDK Asynchronously
  (function(d){
     var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement('script'); js.id = id; js.async = true;
     js.src = "//connect.facebook.net/en_US/all.js";
     ref.parentNode.insertBefore(js, ref);
   }(document));
</script>

<form id="myForm" name="myForm" action="post" onSubmit="return postForm()">
<p><label>Your Message:</label><br/><textarea name="msg"></textarea></p>
<p><label>Post to Facebook?</label><input type="checkbox" value="1" name="toFB" /></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>

CodeIgniter REST API Library Ajax PUT throwing 403 Forbidden

4 votes

I got the rest of the library working fully, just trying to generate api keys and its throwing a 403 forbidden when executed via ajax.

({"status":false,"error":"Invalid API Key."})

I traced it to _remap function under REST_Controller.. almost as if im calling the url incorrectly?

workflow: user visits site1.com -> registers for account -> generates api key for their domain -> key recorded in db -> key displayed

The following form would be on site1.com after they register for an account they would click "generate key".

ajax call:

/**
 * Generate an API Key for Us to use
 */

 $("#submitGetApiKey").click(function(){
    $.ajax({
        url: "http://dev.site1.com/api/key",
        crossDomain: true,
        type: "PUT",
        dataType: "jsonp",
        error: function(XMLHttpRequest, textStatus, errorThrown){
            alert(errorThrown);
        },
        success: function(data){
            for (var i = keys.length - 1; i >= 0; i--) {
                console.log(keys[i]);
            };
        }
    });
 });

REST-SERVER on GitHub: https://github.com/philsturgeon/codeigniter-restserver

look specifically at key.php under application/controllers/api/key.php

Snippet of the key.php file that should relate to this process:

/**
 * Key Create
 *
 * Insert a key into the database.
 *
 * @access  public
 * @return  void
 */
public function index_put()
{
    // Build a new key
    $key = self::_generate_key();

    // If no key level provided, give them a rubbish one
    $level = $this->put('level') ? $this->put('level') : 1;
    $ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1;

    // Insert the new key
    if (self::_insert_key($key, array('level' => $level, 'ignore_limits' => $ignore_limits)))
    {
        $this->response(array('status' => 1, 'key' => $key), 201); // 201 = Created
    }

    else
    {
        $this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error
    }
}

Response/Request Headers

Request URL:http://dev.mapitusa.com/api/key
Request Method:PUT
Status Code:403 Forbidden
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:0
Cookie:ci_session=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22e165df34aa4fda5936e940658030f83d%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A118%3A%22Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10_7_3%29+AppleWebKit%2F535.19+%28KHTML%2C+like+Gecko%29+Chrome%2F18.0.1025.3+Safari%2F535.19%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1328291821%3B%7Dac0f163b112dbd3769e67f4bb7122db2
Host:dev.mapitusa.com
Origin:http://dev.mapitusa.com
Referer:http://dev.mapitusa.com/api_test.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.3 Safari/535.19
Response Headersview source
Cache-Control:max-age=0, public
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:69
Content-Type:application/json
Date:Fri, 03 Feb 2012 18:03:54 GMT
Expires:Fri, 03 Feb 2012 18:03:54 GMT
Keep-Alive:timeout=5, max=98
Server:Apache
Set-Cookie:ci_session=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22f2f466f7b97b89f2a9b557d2d9a0dbcc%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A118%3A%22Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10_7_3%29+AppleWebKit%2F535.19+%28KHTML%2C+like+Gecko%29+Chrome%2F18.0.1025.3+Safari%2F535.19%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1328292234%3B%7D6821b96c7e58b55f1767eb265ffdb79e; expires=Fri, 03-Feb-2012 20:03:54 GMT; path=/
Status:403
Vary:Accept-Encoding,User-Agent
X-Powered-By:PHP/5.3.6
X-UA-Compatible:IE=Edge,chrome=1

i ended up finding out the 403 forbidden was because i was not providing an api key to generate keys..

Kind of abiguous as Phil's documentation doesn't state that an existing api key is required before you can generate keys..

I simply created a bogus key in the table in the db and referenced that when calling /key/index?X-API-KEY=boguskey

AJAX Submission Form using Bottle (Python)

4 votes

I'm having some issues getting AJAX communication working using the Bottle framework. This is my first time using AJAX, so it's likely I just have the basics wrong. Hopefully a Bottle/AJAX guru can point this novice in the right direction. Here is the code I'm using:

#!/usr/bin/env python

from bottle import route, request, run, get


# Form constructor route

@route('/form')
def construct_form():
    return '''

<html>
<head>
<script type="text/javascript">

    function loadXMLDoc()
    {
        xmlhttp = new XMLHTTPRequest();
        xmlhttp.onReadyStateChange = function()
        {
            if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
            {
                document.getElementById("responseDiv").innerHTML = xmlhttp.responseText;
            }
        }

    xmlhttp.open("GET", "/ajax", true);
    xmlhttp.send();
    }   

</script>
</head>

<body>

    <form>
        <input name="username" type="text"/>
        <input type="button" value="Submit" onclick="loadXMLDoc()"/>
    </form>
    <div id="responseDiv">Change this text to what you type in the box above.</div>

</body>
</html> 

    '''

# Server response generator

@route('/ajax', method='GET')
def ajaxtest():
    inputname = request.forms.username
    if inputname:
        return 'You typed %s.' % (inputname)
    return "You didn't type anything."

run(host = 'localhost', port = '8080')

There are a few issues here.

  1. Javascript is case sensitive. XMLHTTPRequest should be XMLHttpRequest. You should have seen an error about this in your Javascript console.
  2. onReadyStateChange should be onreadystatechange.
  3. If you fix the above two issues your AJAX call will work, but you will only ever get the 'You didn't type anything.' response. This is because you are using GET. You need to change your code so the form values are posted using the POST method.

Also, why aren't you using jQuery to do AJAX? It would make your life much easier. :)

editable textbox after receiving data by JSON

4 votes

I am receiving data from state.jsp by JSON and displaying data in auto.jsp in a textbox having id textbox2.But i am not able to edit that textbox where i am receiving data, why?

//auto.jsp:

 $("#combo1").change(function() {
     // by onchange event of combobox, i am displaying string "anyname"
     // on that below textbox.
     $.getJSON('state.jsp', { combo1Val : $(this).val() }, function(responsedata) {
         $("#textbox2").replaceWith(responsedata.name);
     });
 });
 // i am displaying "anyname" here, but why i am not able
 // to edit this text box after displaying? I have not set it to readonly
 <input type="text" id="textbox2" name="2ndtextbox/> 

//state.jsp

<%@page import="net.sf.json.JSONObject"%>
<%@page import="net.sf.json.JSONArray"%>
<%
JSONObject arrayObj= new JSONObject();
       arrayObj.put("name","anyname");// displaying "anyname" in that textbox
      response.setContentType("application/json");
      response.getWriter().write(arrayObj.toString());
%>

I am displaying string "anyname" in that text box but i am not able to edit this textbox anymore why? I have not set it to readonly. Any help

.replaceWith() replaces the matched set by the value specified (text, a dom element, a jquery object). So in your code, you are replacing the while INPUT element with your response data instead of setting its value

To set the value of a form element, use the .val() method:

$("#textbox2").val(responsedata.name);

How to Make an AJAX Get Request using MVC 3.0 RAZOR?

4 votes

I have the below code which runs when an option in the drop down list is changed:

function ddlSqlList_onchange(listItemId) {
    $.get('/SqlReportList/SqlQuery',         
    { 
        "listItemId": listItemId 
    },
    function (data) { 
        alert('succeeded'); 
        $('#tbSqlQuery').text(data); 
    });}

"SqlReportList" is my Controller, SqlQuery is an Action and listItemId is an input parameter for that action.

     public string SqlQuery(string listItemId)
            {
// code here
}

It works locally fine but when deployed unto our dev server, it doesn't work.

I realized that the url has to change to "/ApplicationName/SqlReportList/SqlQuery" to make it work on the server.

So how to retrieve the application path at runtime?

Hope the question is clear.

Thanks,

Which version of MVC are you using..

In MVC 3.0 with Razor you could use:

@Url.Action("SqlQuery","SqlReportList")

or you could use:

@Server.MapPath("~")

to get the base address of your app and then build it up yourself. Server.MapPath works in the controller too if that helps.(Seems the Url class is available in the controller too)

(from memory)

edit for comment:

If you are in a .cshtml file it would look like the following:

function ddlSqlList_onchange(listItemId) {
    $.get('@Url.Action("SqlQuery","SqlReportList")',         
    { 
        "listItemId": listItemId 
    },
    function (data) { 
        alert('succeeded'); 
        $('#tbSqlQuery').text(data); 
    });}

How to submit form using Ajax request in Liferay?

4 votes

I am newbie in liferay portal. I have developed one portlet in liferay for demo. I used inter portlet communication in this example. What i am doing is:- I have one search portlet in which i am having one textfield for search. When i click on search button it fetches the data from the database and display that data using search-contained in another portlet. I used ProcessEvent and ActionEvent annotation for this project.

Now what i want is when i click on the search button then the page should not be refresh(i.e i wish to use the concept of AJAX) and data should be displayed on the other portlet.

Code Snippet

Portlet A - view.jsp

<%@include file="/html/init.jsp"%>
<portlet:defineObjects />

<!--

<portlet:actionURL var="actionURL" name="pitchBall"></portlet:actionURL>

//-->
**Change to Resource URL**
<portlet:resourceURL var="resourceURL">
</portlet:resourceURL>


<aui:form method="POST" action="<%= resourceURL%>" name="    <portlet:namespace>fm1</portlet:namespace>">
    <aui:input name="search" id="search" />
    <aui:button type="submit" name="Search" value="Search" />
</aui:form>

Portlet A - SearchPortlet Class

package com.test;

/**
 * Portlet implementation class SearchPortlet
 */
public class SearchPortlet extends GenericPortlet {

    @Override
    public void render(RenderRequest request, RenderResponse response)
            throws PortletException, IOException {
        // TODO Auto-generated method stub
        super.render(request, response);

    }

    @ProcessAction(name="pitchBall") 
    public void pitchBall(ActionRequest request, ActionResponse response) throws SystemException {
        String name = ParamUtil.getString(request, "search");       
        QName qName = new QName("http://liferay.com/events", "ipc.pitch");
        response.setEvent(qName, name);
    }

    public void init() {
        editJSP = getInitParameter("edit-jsp");
        helpJSP = getInitParameter("help-jsp");
        viewJSP = getInitParameter("view-jsp");
    }

    public void doEdit(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        include(editJSP, renderRequest, renderResponse);
    }

    public void doHelp(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        include(helpJSP, renderRequest, renderResponse);
    }

    @Override
    public void doView(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {
        //super.doView(renderRequest, renderResponse);
        System.out.println("In doView code");

        renderResponse.setContentType(renderRequest.getResponseContentType());

        // file to display...
        String url = "/html/searchportlet/view.jsp";

        // read the above file and output it...
        getPortletContext().getRequestDispatcher(url).include(renderRequest, renderResponse);
        //include(viewJSP, renderRequest, renderResponse);
    }

    @Override
    public void serveResource(ResourceRequest request, ResourceResponse response)
            throws PortletException, IOException {
        // TODO Auto-generated method stub
        //super.serveResource(request, response);
         System.out.println("In serveResource code");

         response.setContentType("text/html");

         String name = request.getParameter("search");

         // this seems to be the page that was calling...?
         String resourceID = request.getResourceID();
         System.out.println("resourceId was : " + resourceID);


         System.out.println("message was : " + name);
         PrintWriter writer = response.getWriter();

         writer.print(name);
    }

    protected void include(
            String path, RenderRequest renderRequest,
            RenderResponse renderResponse)
        throws IOException, PortletException {

        PortletRequestDispatcher portletRequestDispatcher =
            getPortletContext().getRequestDispatcher(path);

        if (portletRequestDispatcher == null) {
            _log.error(path + " is not a valid include");
        }
        else {
            portletRequestDispatcher.include(renderRequest, renderResponse);
        }
    }

    protected String editJSP;
    protected String helpJSP;
    protected String viewJSP;

    private static Log _log = LogFactoryUtil.getLog(SearchPortlet.class);

}

Portlet B - view.jsp

<%@include file="/html/init.jsp"%>
<portlet:defineObjects />

<%
String name = (String)renderRequest.getParameter("name");
%>

<liferay-ui:search-container
    emptyResultsMessage="author-empty-results-message">

    <liferay-ui:search-container-results
        results="<%= KeyurAuthorLocalServiceUtil.getStudentByName(name) %>" />

    <liferay-ui:search-container-row className="com.test.model.KeyurAuthor">

        <liferay-ui:search-container-column-text name="authorId"
            property="authorId" />
        <liferay-ui:search-container-column-text name="authorName"
            property="authorName" />
        <liferay-ui:search-container-column-text name="authorEmail"
            property="authorEmail" />
    </liferay-ui:search-container-row>

    <liferay-ui:search-iterator></liferay-ui:search-iterator>


</liferay-ui:search-container>

Portlet B - SearchResultPortlet Class

/**
 * Portlet implementation class SearchResultPortlet
 */
public class SearchResultPortlet extends GenericPortlet {

    public void init() {
        editJSP = getInitParameter("edit-jsp");
        helpJSP = getInitParameter("help-jsp");
        viewJSP = getInitParameter("view-jsp");
    }

    @ProcessEvent(qname="{http://liferay.com/events}ipc.pitch")
    public void catchBall(EventRequest request, EventResponse response) {
        Event event = request.getEvent();
        String name = (String)event.getValue();
        response.setRenderParameter("name", name);
    }

    public void doEdit(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(editJSP, renderRequest, renderResponse);
    }

    public void doHelp(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(helpJSP, renderRequest, renderResponse);
    }

    public void doView(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(viewJSP, renderRequest, renderResponse);
    }

    protected void include(
            String path, RenderRequest renderRequest,
            RenderResponse renderResponse)
    throws IOException, PortletException {

        PortletRequestDispatcher portletRequestDispatcher =
            getPortletContext().getRequestDispatcher(path);

        if (portletRequestDispatcher == null) {
            _log.error(path + " is not a valid include");
        }
        else {
            portletRequestDispatcher.include(renderRequest, renderResponse);
        }
    }

    protected String editJSP;
    protected String helpJSP;
    protected String viewJSP;

    private static Log _log = LogFactoryUtil.getLog(SearchResultPortlet.class);

}

When making ajax requests on portal your portlet should implemet

javax.portlet.ResourceServingPortlet

GenericPortlet already does but you want to override it, and instead of using <portlet:actionURL /> you should use <portlet:resourceURL /> fro from action.

And in your setup you should have search form with hidden field for keywords, and on clicking submit button in search portlet you should copy keywords from that form, with IPC, to search results portlet and invoke submit on search result from (without submitting form in search portlet (A)).

Your SearchResultPortlet class should be

public class SearchResultPortlet extends GenericPortlet {
    ...
    public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, java.io.IOException {
       // do search and return result
    }
    ...
}

EDIT: complete example

SearchForm

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class SearchForm extends GenericPortlet {

    @Override
    protected void doView(RenderRequest p_request, RenderResponse p_response) throws PortletException, IOException {
        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/search.jsp").include(p_request, p_response);
    }
}

SearchResult

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;

public class SearchResult extends GenericPortlet {

    @Override
    protected void doView(RenderRequest p_request, RenderResponse p_response) throws PortletException, IOException {
        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/result.jsp").include(p_request, p_response);
    }

    @Override
    public void serveResource(ResourceRequest p_request, ResourceResponse p_response) throws PortletException, IOException {
                    //do your search here and put results in 'result'
        p_request.setAttribute("result", "results for: " + p_request.getParameter("search"));

        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/html.jsp").include(p_request, p_response);
    }
}

search.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

<portlet:defineObjects />

<script type="text/javascript">

    function doSearch() {
        Liferay.fire('searchKeywords', document.getElementById("<portlet:namespace/>search").value);    
    }

</script>

<form>
    <input type="text" name="search" id="<portlet:namespace/>search" />
    <button name="Search" value="Search" onclick="doSearch()" type="button">Search</button>
</form>

result.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>

<portlet:defineObjects />
<portlet:resourceURL var="rurl" />

<script type="text/javascript">
    Liferay.on('searchKeywords', function(event, p_data){
        var A = AUI(); 
        A.use('aui-io-request', function(aui) {
            A.io.request("<%= rurl %>", { 
                method : 'POST', 
                data: {search: p_data},
                dataType : 'html', 
                on : { 
                    success : function() { 
                        AUI().one("#<portlet:namespace/>placeholder").html(this.get('responseData'));
                    } 
                } 
            });
        });
    });
</script>

Search Results:<br />
<div id="<portlet:namespace/>placeholder">
</div>

html.jsp (this is for rendering results)

<%= request.getAttribute("result") %>

How do I index by multiple items in an Array or Object using JavaScript/jQuery?

4 votes

Background

I have an Array of data in a result object returned by an Ajax call. The data looks like this:

{ Name="User1 Name1", FirstName="User1", Id="005400000001234567", more...}
{ Name="User2 Name1", FirstName="User2", Id="005400000001234568", more...}

Where each item looks like this:

{
    Id:"005400000001234567",
    Name:"User Name",
    FirstName:"User",
    LastName:"Name",
    Title:"Manager"
}

Question

I want to be able to retrieve data either by Id (returning a single user) or by Title (returning an array of users). What would be the best way to go about doing that using JavaScript or jQuery?

Example

Here's what I've attempted so far:

function GetAllUsers()
{
    AllUsersById = new Object();

    MyClass.MyAjaxMethod(function(result,event) {
        if(result) { 
            j$(result).each(function(index,item)
            {
                AllUsersById[item.Id] = item;
            });
        }
    });
}

The code I have above is great for indexing by Id, but I'm not sure what to do for Title.

Additional Details

Also, by the way, there are about 1000 records, and I need this to be fairly efficient. (This is one of the reasons I'm getting the data right away, when the document is ready. I'm not an expert on JavaScript or jQuery efficiency, though. Let me know if you have a better way.)

Any ideas? Thanks in advance!

Seems like you're looking for .grep(). Using .grep you could create a generic function that would filter:

function findInJson (json, key, value) {
    return $.grep(json, function (obj) {
        return obj[key] == value;
    });
}

// With your data getting a specific user by id
findInJson(yourJSON, "Id", "005400000001234567");

// Getting a set of users by title
findInJson(yourJSON, "Title", "Manager");

How does Twitter to display my profile instantly?

4 votes

Context

I realized that in Twitter, the profile page is displayed in different ways depending on how it is called:

  1. By clicking the profile link in the menu, the DOM and the latest tweets are loaded, and the page is displayed in ~4 seconds. Each time.
  2. Using the keyboard shortcut GP (or the link on the left), the page is displayed instantly.

Details

  • I noticed that the profile must have been recently displayed for GP instantly displays the page.
  • By closing and opening the browser, the profile must be displayed again for GP instantly displays the page.

Investigation

So at first I thought Twitter could use a serverside session variable to store data. Then I discovered a use of localStorage in the Twitter source code. I confess, DOM storage is unfamiliar to me and the Twitter JavaScript code is unreadable. So I don't sure they use localStorage to store the profile.

Question

Any hypothesis, infos or links about Twitter DOM storage / session storage?

This is an interesting question, so I went to twitter, and did some investigation myself.

Clicking on my profile name, the link is done with AJAX. I see my timeline getting downloaded. But, the page is already loaded in advance, so my information is also already downloaded.

By clicking on the link on the left, or with GP you just display the page already loaded (hidden, or in JavaScript object, so in memory). It will just display your profile already downloaded, and by AJAX download the feed (JSON).

The URL will change from https://twitter.com/#!/ to https://twitter.com/#!/wraldpyk (in my case).

When you click your profile in the menu (top right) you go to https://twitter.com/wraldpyk. This will re-open the page, and will download everything. Note you will get redirected to https://twitter.com/#!/wraldpyk, and meanwhile your timeline also gets downloaded (open FireBug and see the images and feeds getting downloaded)

As far as I can tell, no local storage (except in JavaScript, like everyone does) is done. All data is constantly downloaded on new page load.

Same thing happens when you type gh when on your profile. (And also with all other shortcuts)

Cross server webservice using ajax

4 votes

Hi stackoverflow users.

My server setup is the following: A webserver with access on http/80 running www.domain.com A app server with access to the internal network (db etc.) running a webservice

I have this simple little server setup problem.

Now I want to call my webservice from a ajax script from a website on my webserver. But since my application server does not have access to the internet this will (in my mind) not be possible since the javascript (running in the end-users browser) shoud have access to that webservice.

I came up with the solution by inventing a webservice on the webserver calling the webservice on my application server, but thats a odd solution, does any of you have a idea how to solve this?

I don't think you can do this. You will have to provide some thing on WebServer using which end user can access your App Server.

You have multiple options for this

  • PageMethods
  • Web Services on WebServer which will relay ajax calls to the App Server

Hope this info helps you.

Change browser address bar URL with jQuery

3 votes

Is it possible to change the URL in the browser address bar directly with jQuery without refreshing the page or redirecting to the changed url?

Or is this not possible due to security issues?

For example, I have a list which is jquery.ajax driven so no post backs/screen refreshes. The list has a paging element. So for the first page the url in the browser address bar is:

http://company/list.php?page=1

If I click on page 2 from the paging section of the list, it displays the list based on page 2, but as this is happening ajax style without refreshing/posting back, the browser url remains at

http://company/list.php?page=1

I want to be able to change it to

http://company/list.php?page=2

without posting back/redirecting to the new url

Is this not possible?

This can only be done in more modern browsers (Chrome, Safari, FF4+, and IE10pp3+)

See this question: Updating address bar with new URL without hash or reloading the page for information on how to do it.

jQuery deferred AJAX calls: possible scope issue

3 votes

I'm making multiple AJAX requests inside a loop. Here's my code for that:

// set defaults
var app_id = 'xxx';
var secret = 'yyy';
var auth_token = 'zzz';

// list of items to call in the API
var insights_to_check = ['application_permission_views_top_unique', 'application_installation_adds_unique', 'application_installation_removes_unique'];
var json_responses = {};

var ajax_calls = [];

// construct the API url
for (var i in insights_to_check) {
    var insight = insights_to_check[i];
    var url = 'https://graph.facebook.com/' + app_id + '/insights/' + insight + '?' + auth_token;
    ajax_calls.push(get_json(url, insight));
}

// when all ajax requests are done, call the process function
$.when.apply($, ajax_calls).done(function(){
    process_json(json_responses);
});

// collect responses to the ajax request in a global object
function get_json(url, insight) {
    $.getJSON(url, function(json) {
        json_responses[insight] = json;
    });
}

// show the global object
function process_json(all_json) {
    console.log(all_json);
}

The code has a weird problem: the first time it runs, I can see the AJAX requests firing and returning, but when process_json() gets called, it logs undefined. If I manually invoke it a second time, it returns the json as expected.

It seems that my deferred callback is firing before the AJAX requests are complete, or at least. before they've been able to assign their response to the global json_responses object. Can anyone see anything glaringly obvious here?

I'm not sure why it logs undefined (it does not for me if I omit the Ajax calls) but one problem with your code is that the .done() callback is executed immediately as ajax_calls is an empty array.

You have to return the return value of $.getJSON from get_json:

function get_json(url, insight) {
    return $.getJSON(url, function(json) {
        json_responses[insight] = json;
    });
}

That might solve the issue.