Grails ; remoteFunction with dynamical update cause null in HTML page - ajax

I have an issue concerning the use of a remoteFunction component in java script function ; I am using Grails 1.3.7.
I have few div in a page which contain a div I want to update. Each div I want to update has its own id (fullUrlSaProfilDivX) where X is and unique ID in the page.
I want to update two div (one after one).
I created a java script function :
<g:javascript>
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
${ remoteFunction (action:"delete", update:'fullUrlSaProfilDiv'+urlSaId, controller:"profilAssoc", params:'\'id=\'+profilAssocId', options:[asynchronous:false]) };
${ remoteFunction (action:"listUrlSaProfil", controller:"profilAssoc", update:'lightUrlSaProfilDiv'+urlSaId, params:'\'urlSa.id=\'+urlSaId') };
};
</g:javascript>
Called by a link :
I want to update the div linked with the button (linkage with unique id).
I can't figure out why in the generated page I got null instead of the id and the div is not refresh :
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
new Ajax.Updater('fullUrlSaProfilDivnull','/_Pong2WAR/profilAssoc/delete',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});;
new Ajax.Updater('lightUrlSaProfilDivnull','/_Pong2WAR/profilAssoc/listUrlSaProfil',{asynchronous:true,evalScripts:true,parameters:'urlSa.id='+urlSaId});;
};
Am I doing something wrong ? How can I pass the id of the div I want to refresh and add it refreshed ?
Thank you for having a look !
Benjamin

ahah, you are mixing up javascript and gsp. I did it many times also, it can be tricky to find out!
In your case urlSaId is a javascript var, but you are using it in a GSP function call so it will be null....
Unfortunatly the workaround is not easy since the remoteFunction won't let you concatenate properly the javascript variable in the update since what you want is:
new Ajax.Updater('fullUrlSaProfilDiv'+urlSaId,'/_Pong2WAR/profilAssoc/delete',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});
What i suggest is to build directly this Ajax.Updater(..) without the use of remoteFunction (or something similar):
<g:javascript>
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
new Ajax.Updater('fullUrlSaProfilDiv'+urlSaId,'${createLink(action:"delete", controller:"profilAssoc")}',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});
new Ajax.Updater('lightUrlSaProfilDiv'+urlSaId,'${createLink(action:"listUrlSaProfil", controller:"profilAssoc")}',{asynchronous:true,evalScripts:true,parameters:'urlSa.id='+urlSaId});;
};
</g:javascript>
On a side note now, i am always using jquery instead, it simplifies all ajax in your GSP.

replace remoteFunction by
jQuery.ajax({
type:'POST',
data:'territorio='+territorio_id+'&anho='+anho+'&id='+firstIndicador+'&div='+divId+'&divmap='+divmap,
url:'/observatoriograils/eje/indGeneralporAnhoyTerritorio',
success:function(data,textStatus){jQuery('#'+divId).html(data);},
error:function(XMLHttpRequest,textStatus,errorThrown){}});
where data are the parameters, url is /url/controller/function and divId is the name of div for update

Related

Dojo.query foreach accessing element value

I'm a newbie in Dojo framework, so I hope my question is not really dumb.
I have a link in my web page :
Delete
The variable "index" is well defined, no issue with that.
Then, I've written this piece of code to add an action to the onclick event on my link and a JS function to call before the submit :
dojo.query("a[name=supprimerEnfant_name]").forEach(function(element) {
Spring.addDecoration(new Spring.AjaxEventDecoration({
formId: "form_id",
elementId: element.id,
event: "onclick",
beforeSubmit: function(){
jsFunctionToCall(element.value);
},
params: { _eventId: "deleteEvent", fragments:"frag"}
}))
});
In my jsFunctionToCall, I can get the element.id (checked and it's OK) but the element's value is null and I can't figure out why.
I'm probably missing something important, could you help me with that ?
Thanks in advance.
You should be aware that element.value only works with elements where it's part of the DOM, defined by W3C. So, if you look at the HTMLInputElement interface (used by form elements), you will see that it clearly has a property called value, referencing to the the value of the element.
However, the same is not true for an HTMLAnchorElement. This means the proper way to retrieve the value of the value attribute, is by selecting the attribute itself, using the getAttribute() function or by using the the dojo/dom-attr Dojo module.
For example:
require(["dojo/query", "dojo/dom-attr", "dojo/domReady!"], function(query, domAttr) {
query("a").forEach(function(element) {
console.log(element.id);
console.log(domAttr.get(element, "value")); // This will work
});
});
Demonstration: JSFiddle
The dojo query will return the domNode references always. Anyhow, the anchor element is a HTML element. So, this is nothing to do with Dojo and lets see whats wrong with the JS part.
The "value" is not a standard attribute of anchor element. So, the additional attributes need to be accessed using "getAttribute" method.
i.e. in your case,
element.getAttribute('value')

Running a javascript after ajax load

I'm using jQuery UI. I'm loading some content in a dialog box over AJAX. After inserting the content from the server, I need to make modifications to the document. I am using the .live() function on my link; I thought this would enable me to use Js after loading the content over ajax, but it's like the content I just loaded isn't a part of the document. Any help very much appreciated.
Are you adding the bindings (lives) in the success function of the ajax call?
If so I had the same issue, I'll try to explain what I figured out:
$.post('callURL', function(data){
// Let's say data returned from server is an ID of a div I have to hide
// by clicking on some_link
$('#some_link').live('click',function(){
$('#'+data).hide();
});
});
This won't work because the code inside the 'live' function is executed on click and at that time the 'data' value is gone.
To make it work I made a global variable 'ID' which I set in the success function and then called in the 'live' function again like this:
var ID;
$.post('callURL', function(data){
// Let's say data returned from server is an ID of a div I have to hide
// by clicking on some_link
ID = data
$('#some_link').live('click',function(){
$('#'+ID).hide();
});
});

how to fake a click on a dynamic element?

On a static element, to fake a click, I use
$(selector).click();
But how can I do the same thing on a dynamic element (resulted from an ajax call)?
The same...:
$(selector).click();
Why didn't you try it first?
P.S. it is not called fake a click, it's called trigger the click event.
$(selector).trigger('click'); == $(selector).click();
Update
You need to bind that element a callback to the event in order it to work:
$(selector).click(function(){...});
$(selector).click();
If you want it to have the the click callback you assigned to the static elements automaticlly, you should use on\ delegate (or live but it's deprecated) when you attach the click callback.
$('body').on('click', 'selector', function(){...})
instead if body use the closest static element the holds that selector elements.
See my DEMO
within your ajax success function try your code:
$(selector).click();
Basing this on your previous question : How can I select a list of DOM objects render from an AJAX call?
$(document).ready(function(){
var listItems = $('#myList li a');
var containers = $('#myContainer > div');
listItems.click(function(e){//do someting
});
etc...
If the elements you are trying to attach a click handler to are supposed to be inside any of the two variables above then you WILL have to update those variables after the elements are inserted into the DOM, as it is right now only elements that exists during first page load will be inside those variables.
That is the only reason I can think of why something like :
$(document).on('click', listItems, function(e) {//do something
});
will not work!
Don't know if I understand (I'm french sorry...)
But try :
$(selector).live('click',function(){}); // deprecated it seems
Demo of gdoron with live() : http://jsfiddle.net/Rx2h7/1/
use on() method of jquery,
staticElement.on('click', selector, function(){})
on method generates click event on dynamically created element by attaching it to the static element present in the DOM .
For further reference check this out -- https://api.jquery.com/on/

TinyMCE not working in http request xhr ajax generated page

So i I have a page that contains links that call an httpRequest. The request calls a php file that grabs data from mysql and pre populates a form which is then returned to the browser/webpage. My problem is that when the page is returned to the browser via the httpRequest/ajax the text area does not display the tinymce editor, it just displays a normal text area. It looks like my request and ajax is working fine the text area just doesn't have the tinycme editor on it.
When i don't use ajax it works fine but when i put it in a separate file and call it via ajax it doesn't bring in the tinymce editor.
Does anyone know how to fix this problem so that my ajax generated page displays the text area with the tinymce editor. Thank you.
Lets presume that your thinyMCE instance is initialized with code below
// initialize tinyMCE in page
tinyMCE.init({
mode: "textareas",
theme: "advanced"
});
and you have some kind of button somewhere in the page. For purpose of this tip, i will not give it any ID but you may. Now, using jQuery you can easily attach event handler to that button which will call through AJAX your server and take content which you want to put tinyMCE editor. Code which will do such job would look somehow like below.
$(function() {
$("button").bind("click", function() {
var ed = tinyMCE.get('content');
ed.setProgressState(1); // Show progress
$.getJSON('/page/12.json', { /* your data */
}, function(data) {
ed.setProgressState(0); // Hide progress
ed.setContent(data["body"]);
}
});
});
});
You can see that on button.click ajax will call url /page/12.json which will return JSON as response. bare minimum of that response could be:
{
title: "Page title",
body: "<html><head><title>Page title</title>......</html>"
}
I attached anonymous function as callback which will handle response from server. and hide progress indicator which is shown before ajax call.
About JSON
JSON is shorten of JavaScript Object Notation. It is JavaScript code!!! So don't be confused about it. Using JSON you can make javascript object which can have attributes you can use later in your code to access particular peace of data which that object "holds". You can look at it as some kind of data structure if it is easier to you.
Anyway, to show you how this JSON can be created by hand look at examples below
var data = new Object();
data.title = "Page title";
data.body = "<html....";
or
var data = {
title: "page title",
body: "<html...."
};
it is very same thing.
If you want to learn more about JSON point your browser to http://json.org.
===== alternative =====
Alternative to json solution could be just plane ajax call to server and response can be plain HTML (from your question I can assume that you have something like this already). So instad of calling $.getJSON you can use $.get(url, callback); to do same thing. The code at the top of my answer will not dramatically change. Instead of geting JSON in response you will get string which is HTML.
----------- BOTTOM LINE -------
I prefer JSON since it can be easily extended later with other attributes, so there is no painful code changes later ;)
Problem here will be that when you return the full page and render it using the ajax response, your tinymce instance has not been shut down before.
In order to do this you can call this small piece of code before you render the ajax response:
tinymce.execCommand('mceRemoveControl',true,'editor_id');
In this case the editor should initialize correctly. You are not allowed to initialize a tinymce editor with the same id before shutting the first one down.
Strangely i ran into this problem yesterday. Following code should work, but YMMV. Trick is to use the correct steps in ajax events. I used the Regular TinyMCE and made use of the jQuery library already included.
Following goes into your tinyMCE initialization tinyMCE.init() . All of the below block should be outside the document.ready.
myTinyInit = {
//.......All essential keys/values ...........
setup : function(ed) {
ed.onChange.add(function( ed ) {
tinyMCE.triggerSave();
}) }
//.....................
};
// Init the tinyMCE
tinyMCE.init(myTinyInit);
This ensures the content is being saved regularly onto the textarea that holds the value. Next step is setting up the request events.
Normally tinyMCE mceAddControl before the ajax post and mceRemoveControl after the ajax success should do the trick. But I found that often does not work.
I used the form as the jQuery selector in my case.
jQuery( '.myForm' )
.find( 'textarea#myTextArea' )
.ajaxStart(function() {
// If you need to copy over the values, you can do it here.
// If you are using jQuery form plugin you can bind to form-pre-serialize event instead.
// jQuery( this ).val( tinyMCE.get( jQuery( this ).attr( 'id' )).getContent() );
}).ajaxSend( function() {
// ! - step 2
// My case was multiple editors.
myEds = tinyMCE.editors;
for( edd in myEds ) {
myEds[ eds ].remove();
}
// tinyMCE.get( 'myTextarea' ).remove();
// strangely mceRemoveControl didnt work for me.
// tinyMCE.execCommand( 'mceRemoveControl', false, jQuery( this ).attr('id'));
}).ajaxSuccess(function() {
// Now we got the form again, Let's put up tinyMCE again.
txtID = jQuery( this ).attr( 'id' );
// ! - step 3
tinyMCE.execCommand( 'mceAddControl', false, txtID );
// Restore the contents into TinyMCE.
tinyMCE.get( txtID ).setContent( jQuery( this ).val());
});
Problems i came across :
Using mceRemoveControl always gave me r is undefined error persistently.
If you get a blank tinyMCE editor, check the DOM whether the ID of the textarea is replaced with something like mce_02, this means that TinyMCE is being initialized again or something is wrong with the order. If so, the tinyMCE is duplicated with each save.
if you are new to JS, I recommend using jQuery with the form plugin, it might be easier for you. But do use the regular non-jquery tinyMCE, as it is well documented.
I fixed this problem by recalling the function after the ajax call. In this part of my ajax:
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("Content").innerHTML=xmlhttp.responseText;
tinymce();
Now it works fine.

How to change WebGrid action for getting data (.NET MVC3)

I have a Partial View that renders WebGrid. My controller looks like
public ActionResult Index()
{
return View();
}
public ActionResult GetUserList(int? page, string sort, string sortdir)
{
var model = UserModel.getList(page,sort,sortdir);
return PartialView("_UserList",model);
}
Index.cshtml :
....
#Html.Action("GetUserList")
The problem is that every time I click on grid navigation or sort links it calls Index method. How can I make Webgrid to execute a different action (GetUserList in this case)? I'm sure I can prepend GetUserList to all links in grid using jquery, but I believe it should be a better way.
It's also possible that what I'm doing is completely wrong, so thanks for your suggestions.
After lot of monkeying around and digging (and even fiddling with Reflector with WebGrid's source code), I came to the conclusion that with WebGrid, you cannot control/change the Header link action.
To create the header link URL, the path is taken from HttpContext.Request.Path, so there is no way to customize it to point to a different route.
One very ugly hack would be to tap into to jQuery Ajax's events (since the header link uses jQuery.load to sort) and overwrite the URL:
Album Id
Better solution would be to use:
Telerik Grid which lets you specify custom routes and also offers much more flexibility in rendering your layout
or MvcContrib Grid (not sure if this lets you modify header links but definitely offers more flexibility than WebGrid)
#MrChief had the idea above about the ugly hack...I put that together. Here is the main code that I used to do this. It does, indeed, hijack the ajax call before it is put on the wire. The key is to modify the URL that is getting sent because the grid will grab that URL from HttpContext.Request.Path. and plug it into the onclick for the anchor element.
I put this into my main common.js and will simply attach a function to capture the ajaxSend event which happens just before the data is sent.
// Used to hijack the sending of all AJAX calls. Before it sends the call to the server, it checks to see if the
// active element (the element that prompted the call) is marked with a given class. If so, then it will perform
// the given operation.
$(document).ajaxSend(function (event, jqXHR, ajaxOptions) {
var activeElement = document.activeElement;
if ($(activeElement).attr('redosorturl') != null) {
// If this is a sort anchor link from a grid that needs to have the sort link redone, do it here.
// the code is in the eipGrip.js file.
if ($(activeElement).attr('redosorturl').toString() == 'redoSortURL') {
var newURL = RedoGridSortURL(activeElement, ajaxOptions.url.toString());
ajaxOptions.url = newURL.toString();
}
}
return false;
});
When rendering the page, I have marked the tag in column header that contains the incorrect URL with a class named "redosorturl', so I know when I hijack the ajax call, the operation has to be done on this element. I then call a custom function that gives me the correct URL, then the ajaxOptions.url is then rewritten with that new URL.
I have to pass the activeElement to that rewrite function so I can traverse up the DOM to get the grid information, where I have put data like the controller and action method that is used along with and IDs and other info that I use for the URL. Likewise, I pass in the current url string because the grid will inject a token at the end of the url that I parse off and put on the new url.
Your conclusion isn't right. You just need to wrap your webgrid in a Get form:
using (Html.BeginForm("GetUserList", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
var grid = new WebGrid(
...
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
The hiddens are so that the sort dir and sort field end up in parseable form in the querystring. You end up with urls like localhost/ThingaMaBob/GetUserList?someotherfields=whatever=&sort=city&sortdir=ASC
If you remove [HttpPost] attribute and let the route come to the same function. you'll find the Request["page"] value in your method. this will allow you to put a check on Request["Page"] value.

Resources