I am building a small DNN MVC module whereby I need a user to upload file which will be processed server side.
When the form is posted back, the view model is posted back fine, but the file never is. Request.Files is always 0.
I even simplified it so all I had on the module was a simple file input and submit button but that failed as well.
I would hate to have to revert back to .ascx controls to get this to work.
I am testing this as an unregistered user, therefore there is no authentication checking in the controller.
See code below:
View
#inherits DotNetNuke.Web.Mvc.Framework.DnnWebViewPage<NM.Modules.FlexEventsCreate.Models.FlexEventViewModel>
#using DotNetNuke.Web.Mvc.Helpers
<input type="file" id="fileUp"/>
<input type="submit" id="btnSubmit" />
Controller
[DnnHandleError]
public class ItemController : DnnController
{
[HttpPost]
public ActionResult ShowForm(FlexEventViewModel flexEvent)
{
if (ModelState.IsValid)
{
var file = Request.Files;
if (file.Count != 0)
{
//do something
}
//return RedirectToDefaultRoute();
}
return View(flexEvent);
}
}
The rendered DNN HTML looks like this (I have simplified it)
<form method="post" action="/Test" id="Form" enctype="multipart/form-data">
<!-- Begin Content areas -->
<div>
<div class="row">
<div class="medium-9 columns">
<div id="dnn_LeftPane">
<div class="DnnModule DnnModule-DnnModule-747">
<a name="747"></a>
<div class="DnnF_Title_h1 SpacingBottom">
<h1><span id="dnn_ctr747_dnnTITLE_titleLabel" class="TitleH1"></span>
</h1>
<div id="dnn_ctr747_ContentPane">
<!-- Start_Module_747 -->
<div id="dnn_ctr747_ModuleContent">
<div id="dnn_ctr747_ShowForm_Prog" class="RadAjax RadAjax_Default" style="display:none;">
<div class="raDiv">
</div>
<div class="raColor raTransp">
</div>
</div>
<div class="RadAjaxPanel" id="dnn_ctr747_dnn_ctr747_ShowForm_UPPanel">
<div id="dnn_ctr747_ShowForm_UP">
<!-- 2013.2.717.40 -->
<div id="mvcContainer-747">
<input type="file" id="fileUp">
<input type="submit" id="btnSubmit">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
I did do an upload in an MVC module using the dropzone jquery component - which may help you. See my sample Restaurant Menu project on github.
First, include the dropzone script and css:
#using DotNetNuke.Web.Client.ClientResourceManagement
#{
ClientResourceManager.RegisterStyleSheet(Dnn.DnnPage, "~/DesktopModules/MVC/DotNetNuclear/RestaurantMenu/Resources/dropzone/css/dropzone.css");
ClientResourceManager.RegisterScript(Dnn.DnnPage, "~/DesktopModules/MVC/DotNetNuclear/RestaurantMenu/Resources/dropzone/js/dropzone.min.js", 100);
}
Then place a container div for the upload component:
<div id="dZUpload" class="uploadform dropzone no-margin dz-clickable">
<div class="dz-default dz-message"></div>
</div>
Initialize the component and tell it what type and how many files can be uploaded:
$("#dZUpload").dropzone({
acceptedFiles: "image/jpeg,image/png,image/gif",
url: '#Url.Action("Upload", "Menu")',
maxFiles: 1, // Number of files at a time
maxFilesize: 1, //in MB
addRemoveLinks: true,
maxfilesexceeded: function (file) {
alert('You have uploaded more than 1 Image. Only the first file will be uploaded!');
},
success: function (response) {
}
});
Change the acceptedFiles to the mimetypes you are restricting ("application/pdf", etc). Change the maxFiles to limit how many files they can upload at a time.
Write an MVC controller action to respond to the Dropzone file upload url. You can see it expects an action method "Upload" on the controller "Menu" (MenuController.Upload):
public JsonResult Upload()
{
string imageUrl = string.Empty;
string imgPath = Server.MapPath("~/Portals/0/Restaurant/");
if (!Directory.Exists(imgPath))
{
Directory.CreateDirectory(imgPath);
}
foreach (string s in Request.Files)
{
var file = Request.Files[s];
if (file.ContentLength > 0)
{
string fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(imgPath, fileName);
file.SaveAs(path);
imageUrl = string.Format("/Portals/0/Restaurant/{0}", fileName);
}
}
return Json(new { img = imageUrl, thumb = imageUrl });
}
Basically i want to automatically POST content to a form without using XMLHttpRequest.
The site works fine but there is a problem with the browser history when getting back to the page by clicking the browser back button.
My site looks like this:
<!DOCTYPE html>
<html>
<head runat="server">
<title>History Problem</title>
<script>
window.onload = function () {
try {
if ("<%= Request.Form["hiddenField1"] == null %>" == "True") {
var theForm = document.getElementById("myForm");
var theIframe = document.createElement('iframe');
theIframe.id = theIframe.name = "myIframe";
document.body.appendChild(theIframe);
theForm.hiddenField1.value = "SomeValue";
theForm.submit();
}
}
catch (e) {
var x = e;
}
};
window.onunload = function () { };
</script>
</head>
<body>
<form runat="server" id="myForm" action="" target="myIframe" method="post">
<input type="hidden" id="hiddenField1" name="hiddenField1" />
</form>
<br />
External Link
</body>
</html>
The trace shows that there is a GET and a POST request. everything is fine so far. But if you click on "External Link" (google.com) and then back to the site, you'll see two history entries for the test site. Also the google.com history entry seems to be gone.
This happens only in Internet Explorer.
Any ideas?
I've created an order form, of which has a large array of fields. I'm now adding coupon functionality so that we can start to give customers discount codes etc.
What's the best way of submitting a single field (the coupon code) using ajax after a "apply coupon" button has been clicked so that the price can be updated on the front end probably using UJS(?) - obviously the final price calculation would happen on the backend?
Cheers.
I would recommend using a JS framework to do Ajax requests. If you JQuery, then you can use the example given to understand how to do a simple post.
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<form action="/" id="searchForm">
<input type="text" name="s" placeholder="Search..." />
<input type="submit" value="Search" />
</form>
<!-- the result of the search will be rendered inside this div -->
<div id="result"></div>
<script>
/* attach a submit handler to the form */
$("#searchForm").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
/* get some values from elements on the page: */
var $form = $( this ),
term = $form.find( 'input[name="s"]' ).val(),
url = $form.attr( 'action' );
/* Send the data using post and put the results in a div */
$.post( url, { s: term },
function( data ) {
var content = $( data ).find( '#content' );
$( "#result" ).empty().append( content );
}
);
});
</script>
</body>
</html>
I have a method in my controller like this
public string UpdateResource()
{
Thread.Sleep(2000);
return string.Format("Current time is {0}", DateTime.Now.ToShortTimeString());
}
I have a html button in my view page and when I click on that I want a loading gif image to appear and then disappear after 2000ms. Below is my html
<input type="button" id="btnUpdate" value="Update" />
<img id="loading" src="/Images/ajax-loader.gif" alt="Updating ..." />
<div id="result"></div>
How can I call the controller method. I have seen Html.ActionLink but I want this on button click and not a hyperlink.
Please help
First change to UpdateResource method. Now it returns ActionResult:
public ActionResult UpdateResource()
{
Thread.Sleep(5000);
return Content(string.Format("Current time is {0}", DateTime.Now.ToShortTimeString()));
}
We have to hide image when document is loaded so we change image tag to:
<img id="loading" src="../../Content/progress.gif" alt="Updating ..." style="display: none;" />
We have added style="display:none".
Then we are using jQuery:
<script type="text/javascript">
$(document).ready(
function() {
$('#btnUpdate').click(
function() {
$('#loading').show();
$.get('<%= Url.Action("UpdateResource") %>', {},
function(data) {
$('#result').html(data);
$('#loading').hide();
});
}
);
}
);
</script>
When document is loaded, we are setting click action to your button. It shows progress and then uses ajax to get ActionResult. When ActionResult comes back, we are hiding progress and setting content of #result div with returned data.
How do you post data to an iframe?
Depends what you mean by "post data". You can use the HTML target="" attribute on a <form /> tag, so it could be as simple as:
<form action="do_stuff.aspx" method="post" target="my_iframe">
<input type="submit" value="Do Stuff!">
</form>
<!-- when the form is submitted, the server response will appear in this iframe -->
<iframe name="my_iframe" src="not_submitted_yet.aspx"></iframe>
If that's not it, or you're after something more complex, please edit your question to include more detail.
There is a known bug with Internet Explorer that only occurs when you're dynamically creating your iframes, etc. using Javascript (there's a work-around here), but if you're using ordinary HTML markup, you're fine. The target attribute and frame names isn't some clever ninja hack; although it was deprecated (and therefore won't validate) in HTML 4 Strict or XHTML 1 Strict, it's been part of HTML since 3.2, it's formally part of HTML5, and it works in just about every browser since Netscape 3.
I have verified this behaviour as working with XHTML 1 Strict, XHTML 1 Transitional, HTML 4 Strict and in "quirks mode" with no DOCTYPE specified, and it works in all cases using Internet Explorer 7.0.5730.13. My test case consist of two files, using classic ASP on IIS 6; they're reproduced here in full so you can verify this behaviour for yourself.
default.asp
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Form Iframe Demo</title>
</head>
<body>
<form action="do_stuff.asp" method="post" target="my_frame">
<input type="text" name="someText" value="Some Text">
<input type="submit">
</form>
<iframe name="my_frame" src="do_stuff.asp">
</iframe>
</body>
</html>
do_stuff.asp
<%#Language="JScript"%><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Form Iframe Demo</title>
</head>
<body>
<% if (Request.Form.Count) { %>
You typed: <%=Request.Form("someText").Item%>
<% } else { %>
(not submitted)
<% } %>
</body>
</html>
I would be very interested to hear of any browser that doesn't run these examples correctly.
An iframe is used to embed another document inside a html page.
If the form is to be submitted to an iframe within the form page, then it can be easily acheived using the target attribute of the tag.
Set the target attribute of the form to the name of the iframe tag.
<form action="action" method="post" target="output_frame">
<!-- input elements here -->
</form>
<iframe name="output_frame" src="" id="output_frame" width="XX" height="YY">
</iframe>
Advanced iframe target use
This property can also be used to produce an ajax like experience, especially in cases like file upload, in which case where it becomes mandatory to submit the form, in order to upload the files
The iframe can be set to a width and height of 0, and the form can be submitted with the target set to the iframe, and a loading dialog opened before submitting the form. So, it mocks a ajax control as the control still remains on the input form jsp, with the loading dialog open.
Exmaple
<script>
$( "#uploadDialog" ).dialog({ autoOpen: false, modal: true, closeOnEscape: false,
open: function(event, ui) { jQuery('.ui-dialog-titlebar-close').hide(); } });
function startUpload()
{
$("#uploadDialog").dialog("open");
}
function stopUpload()
{
$("#uploadDialog").dialog("close");
}
</script>
<div id="uploadDialog" title="Please Wait!!!">
<center>
<img src="/imagePath/loading.gif" width="100" height="100"/>
<br/>
Loading Details...
</center>
</div>
<FORM ENCTYPE="multipart/form-data" ACTION="Action" METHOD="POST" target="upload_target" onsubmit="startUpload()">
<!-- input file elements here-->
</FORM>
<iframe id="upload_target" name="upload_target" src="#" style="width:0;height:0;border:0px solid #fff;" onload="stopUpload()">
</iframe>
This function creates a temporary form, then send data using jQuery :
function postToIframe(data,url,target){
$('body').append('<form action="'+url+'" method="post" target="'+target+'" id="postToIframe"></form>');
$.each(data,function(n,v){
$('#postToIframe').append('<input type="hidden" name="'+n+'" value="'+v+'" />');
});
$('#postToIframe').submit().remove();
}
target is the 'name' attr of the target iFrame, and data is a JS object :
data={last_name:'Smith',first_name:'John'}
If you want to change inputs in an iframe then submit the form from that iframe, do this
...
var el = document.getElementById('targetFrame');
var doc, frame_win = getIframeWindow(el); // getIframeWindow is defined below
if (frame_win) {
doc = (window.contentDocument || window.document);
}
if (doc) {
doc.forms[0].someInputName.value = someValue;
...
doc.forms[0].submit();
}
...
Normally, you can only do this if the page in the iframe is from the same origin, but you can start Chrome in a debug mode to disregard the same origin policy and test this on any page.
function getIframeWindow(iframe_object) {
var doc;
if (iframe_object.contentWindow) {
return iframe_object.contentWindow;
}
if (iframe_object.window) {
return iframe_object.window;
}
if (!doc && iframe_object.contentDocument) {
doc = iframe_object.contentDocument;
}
if (!doc && iframe_object.document) {
doc = iframe_object.document;
}
if (doc && doc.defaultView) {
return doc.defaultView;
}
if (doc && doc.parentWindow) {
return doc.parentWindow;
}
return undefined;
}
You can use this code, will have to add proper params to be passed and also the api url to get the data.
var allParams = { xyz, abc }
var parentElm = document.getElementBy... // your own element where you want to create the iframe
// create an iframe
var addIframe = document.createElement('iframe');
addIframe.setAttribute('name', 'sample-iframe');
addIframe.style.height = height ? height : "360px";
addIframe.style.width = width ? width : "360px";
parentElm.appendChild(addIframe)
// make an post request
var form, input;
form = document.createElement("form");
form.action = 'example.com';
form.method = "post";
form.target = "sample-iframe";
Object.keys(allParams).forEach(function (elm) {
console.log('elm: ', elm, allParams[elm]);
input = document.createElement("input");
input.name = elm;
input.value = allParams[elm];
input.type = "hidden";
form.appendChild(input);
})
parentElm.appendChild(form);
form.submit();