I'm trying to use ajax with thymeleaf. I designed a simple html page with two input field. I would like to use addEventHandler for the value of first input text, then I want to send it to controller and make calculation, after that I need to write it in same html form in the second field which returns from controller.
For example:
first input text value -> controller (make calculation) -> (write value) in second input text.
My html page is
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<input type="text" name="Thing" value=""/>
<script th:inline="javascript">
window.onload = function () {
/* event listener */
document.getElementsByName("Thing")[0].addEventListener('change', doThing);
/* function */
function doThing() {
var url = '#{/testurl}';
$("#fill").load(url);
alert('Horray! Someone wrote "' + this.value + '"!');
}
}
</script>
<!-- Results block -->
<div id="fill">
<p th:text="${responseMsg}"/></div>
</div>
</body>
</html>
My controller
#RequestMapping(value = "/testurl", method = RequestMethod.GET)
public String test(Model model) {
model.addAttribute("responseMsg","calcualted value")
return "test";
}
However I cannot call controller from ajax. Could you help me?
There are a few issues with your code. First of all, it looks like you're using the same template for both the initial loading of the application, and returning the calculated result.
You should split these two into different calls if you're using AJAX, since one of the goals of AJAX is that you don't need to reload an entire page for one change.
If you need to return a simple value, you should use a separate request method like this:
#GetMapping("/calculation")
#ResponseBody
public int multiply(#RequestParam int input) {
return input * 2; // The calculation
}
What's important to notice here is that I'm using #ResponseBody and that I'm sending the input to this method as a #RequestParam.
Since you will be returning the calculated value directly, you don't need the Model, nor the responseMsg. So you can remove that from your original request mapping.
You can also remove it from your <div id="fill">, since the goal of your code is to use AJAX to fill this element and not to use Thymeleaf. So you can just have an empty element:
<div id="fill">
</div>
Now, there are also a few issues with your Thymeleaf page. As far as I know, '#{/testurl}' is not the valid syntax for providing URLs. The proper syntax would be to use square brackets:
var url = [[#{/calculation}]];
You also have to make sure you change the url to point to the new request mapping. Additionally, this doesn't look as beautiful since it isn't valid JavaScript, the alternative way to write this is:
var url = /*[[ #{/calculation} ]]*/ null;
Now, your script has also a few issues. Since you're using $().load() you must make sure that you have jQuery loaded somewhere (this looks like jQuery syntax so I'm assuming you want to use jQuery).
You also have to send your input parameter somehow. To do that, you can use the event object that will be passed to the doThing() function, for example:
function doThing(evt) {
var url = [[#{/calculation}]];
$("#fill").load(url + '?input=' + evt.target.value);
alert('Horray! Someone wrote "' + this.value + '"!');
}
As you can see, I'm also adding the ?input=, which will allow you to send the passed value to the AJAX call.
Finally, using $().load() isn't the best way to work with AJAX calls unless you try to load partial HTML templates asynchronously. If you just want to load a value, you could use the following code in stead:
$.get({
url: /*[[ #{/calculation} ]]*/ null,
data: { input: evt.target.value }
}).then(function(result) {
$('#fill').text(result);
});
Be aware that $.get() can be cached by browsers (the same applies to $().load() though). So if the same input parameter can lead to different results, you want to use different HTTP methods (POST for example).
Related
First question:
I want to replace a value in the header. I use --header-HTML header.html for PDF header. For example :
I want to pass 3 values to a PDF:
date
Letter_Number
letter_title
Second question:
Can I use a view for the header? I want to use a view in ASP. For example:
CustomSwitches = "--header-HTML header.cshtml "
About first question
Maybe you could use an HTML page as header, as you actually do, generate new HTML using C# code, and replacing existent HTML file content, with the one you have created, just after generating PDF using Rotativa. The other option I can see, maybe a little bit efficient, because avoids generating all HTML code using C#, is that you use javascript inside your HTML to get this values (not sure if it's completely achievable, since I ignore the origin of the values you mention).
Supposing date value is current date, you could use something like this on your HTML:
<!DOCTYPE html>
<html>
<head>
<script>
function subst() {
var currentDate = new Date();
var dd = String(currentDate.getDate()).padStart(2, '0');
var mm = String(currentDate.getMonth() + 1).padStart(2, '0');
var yyyy = currentDate.getFullYear();
currentDate = dd + '-' + mm + '-' + yyyy;
document.getElementById("dateSpan").innerHTML = currentDate;
}
</script>
</head>
<body onload="subst()">
<div>
Date: <span id="dateSpan"></span>
</div>
</body>
</html>
And on the other side, point to the HTML in custom switches command. Guessing it is located in a folder called PDF, inside Views folder, you could do:
customSwitches = " --header-html " + Server.MapPath("~/Views/PDF/header.html");
I make use of similar code for generating a footer with page number and it works like a charm.
About second question:
I use an MVC action to generate the the partial view that I use as PDF header.
Your code for the custom switches should look like this (using GenerateHeader as action name, PDF as controller and yourModel as the model to be passed to the View, on which you are supposed to store you values):
customSwitches = "--header-html " + Url.Action("GenerateHeader", "PDF", yourModel, Request.Url.Scheme);
For your PDF controller, assuming PdfHeader.cshtml is the view you want to use as PDF header, the code for the action would be as this:
public PartialViewResult GenerateHeader(YourModelType yourModel)
{
return PartialView("PDF/PdfHeader", yourModel);
}
For this PartialView references, remember to include at your controller:
usign System.Web.Mvc;
Hope this helps, if don't, please let me know.
I am send Sending a list from server to thymeleaf through model attribute
friendsOnlineModel.setFriendsOnline(defaultFriendRequestService.getFriendsOnline(user) == null ? Collections.EMPTY_LIST : defaultFriendRequestService.getFriendsOnline(user));
chatModel.setChats(defaultChatService.getUnreadChats() == null ? Collections.EMPTY_LIST : defaultChatService.getUnreadChats());
model.addAttribute("friends_online", friendsOnlineModel);
model.addAttribute("chats", chatModel);
I dont want to display them straight in an "li" tag, but rather pass it to a js function which displays them. Is it possible?
There are a couple ways you can do this. You could use JavaScript inlining, and add the data directly into your page. Similar to the example:
<script th:inline="javascript">
var fiendsOnline = [[${friends_online}]];
var chats = [[${chats}]];
</script>
Then you can deal with them in javascript however you want.
Or, instead of putting these attributes on the model, you can add them to a different controller method annotated with #ResponseBody. Then you call that method using ajax, and Spring will return your object as JSON.
I am new to MVC 3 and have come accross the following scenario:
First let me explain how I have setup my application:
All post backs to the server use jquery ajax which which return a view from the controller that either appended or prepended or replace a targeted div.
The Scenario:
I have come to a point where I would like to upload images but unfortunately because of the jquery ajax posting I cannot get the values for an html in C# Request.Files. I know there are plugins out there to help out with this but I would like to do this myself so I have created an <iframe> which i then use a bit of javascript and post the form targeted to the iframe (old classic way of doing things):
function UploadImg(SACTION) {
alert(SACTION);
validatorform.action = SACTION;
validatorform.target = "fraImage";
validatorform.method = "POST";
validatorform.submit();
}
the SACTION parameter looks like this #Url.Action("UploadFile", "Response"). This all works well as it hits the controllers action method and I can then save the image:
[HttpPost]
public ActionResult UploadFile(string ArticleID)
{
ViewBag.PreviewImage = cFileUploads.UploadFile(ArticleID, Request.Files[0]);
return View("ImagePreview");
}
I would now like to return a view to the iframe (simply to preview the image and then do a couple of other things but this is besides the point)
The View for previewing the Image:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="">
<img alt="" src="#ViewBag.PreviewImage" />
</form>
</body>
</html>
The Problem:
Unfortunately when I return the View (ImagePreview.cshtml) in C# the whole page is refreshed, all I want is for the iFrame to be refreshed. How should I return the view from the controller?
Fiqured out the problem, I had a bit of javascript that was replacing the contents of a div that the iframe was sitting in ... (slap on my forehead). All working perfectly now :)
Will leave this question here just incase ..
In my MVC app, I am returning some Javascript. Howveer, I am using the anti-forgery token on the view, so the rendered result would be
<input name="__RequestVerificationToken" type="hidden" value="E8as+4Ff1u/c/+kuFcNXXCREB5pz5GAfH2krN5RvzURJaHZSApuRc4czZqmoITaKdy0XhN5sFfRzl4ne+wB3PkWOscBWzoIxUk3hGaFwDxRXSbMs8K9IwojEAtV5u57MR7hiSujr6MOTpjjbf5FPaYgO4gmH6lSR9mbSyO2IedI=" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
So there is some HTML to be added to the page then the JS.
The issue is that I get the following notification in Chrome:
Resource interpreted as Script but transferred with MIME type
I need the browser to interpret this as HTML in order to make use of the anti-forgery token.
I have tried putting this on the view:
<%#Page Title="" Language="C#" ContentType="text/xml" %>
Which renders:
<%System.Web.WebPages.DynamicPageDataDictionary`1[System.Object] Title="" Language="C#" ContentType="text/xml" %>
<input name="__RequestVerificationToken" type="hidden"
...
...but the same message persists.
In my controller I have also tried:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(page.clientScript);
return new ContentResult
{
ContentType = "text/xml", // also tried text/html
Content = Encoding.UTF8.GetString(bytes),
ContentEncoding = System.Text.Encoding.UTF8
};
Same issue.
-- UPDATE --
This is how I'm invoking the MVC app to return the text:
// used to load scripts on to the client script using a non-blocking asynch request
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://myserver/MyAppPath/someids';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
If I've understood correctly, you need an MVC action which returns both html and a script tag that can be injected in a page via a <script... include. You also want to render this via an MVC view.
The biggest issue you've missed is that in order to get this content into the calling page, you need to execute document.write from the script - you can't just send back HTML and script in response to the script include - the browser won't understand it, it's expecting javascript only.
There are a few ways to do this - I have written a full suite of ViewContent MVC controller methods, with the same overloads as View which returns the result of a view to a controller action as a string. I can then pass that back as a string literal (useful for html email generation) but also to a javascript encoder.
In this case, you don't need to be so generalist. We can leverage Darin Dimitrov's answer to this SO: Embed MVC Partial View into a document.write JS call and split your view into a View and a partial. The view writes the document.write() skeleton, and the partial view renders the dynamic html you want to be injected into the page. It's unclear if you're using the Anti Forgery Token in the main view which will call the script (in which case it should be rendered as part of the view that it returns) or if you're actually hard-coding it in the script. The second should definitely not be used but I'm writing this answer as if it is, because that appears to be what you want.
First, your partial view (let's call it Fragment.cshtml, put it in ~/Views/Shared)
<input name="__RequestVerificationToken"
type="hidden"value="[ommitted]" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
Second, the host view, called SomeIds.cshtml
#{ Response.ContentType = "text/javascript"; }
document.write('#Html.Raw(HttpUtility.JavaScriptStringEncode(Html.Partial("~/Views/Shared/Fragment").ToHtmlString()))')
Now this view returns a document.write call that injects the HTML returned by the Fragment.cshtml into the page that includes the script.
Are you returning a PartialView that has all of the markup rendered?
Create a PartialView with your (form and script includes) and in your Controller:
public ActionResult Index(Models.MyModel model)
{
// validate the model if needed
return PartialView("[My PartialView Name]", model);
}
You could put your scripts in separate files, and add the [script src] tags in the PartialView.
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") %>')