Reloading a page and showing a new view in the page - c#

In my AccountController class I have this:
public ActionResult ForgotPassword(ForgotPasswordViewModel fpModel = null)
{
string method = HttpContext.Request.HttpMethod;
if (method == "GET")
{
ViewBag.Status = "CREATE_TASK";
ForgotPasswordViewModel model = this.ForgotPasswordManager.LoadForgotPasswordSettings();
if (model != null && !string.IsNullOrWhiteSpace(model.ForgotPasswordMethod) && model.ForgotPasswordMethod.Trim().ToUpper() == "TASKS")
return View(model);
}
if (method == "POST")
{
ViewBag.Status = "TASK_CREATED";
this.CreateTask(fpModel);
}
return RedirectToAction("ForgotPassword"); // Prob this is wrong?
}
Then in my View I have this:
#model Models.Account.ForgotPasswordViewModel
<div class="container" style="background-color: #f6f6f6">
#if (ViewBag.Status == "CREATE_TASK")
{
<div class="form-group">
<h4 id="SearchWelcomeHeader">Password Request</h4>
</div>
using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { role = "form", #class = "form-horizontal" }))
{
// some textboxes to fill up a password request form go in here
<button id="submit" class="btn btn-primary" style="width: 20%">Submit</button>
<button id="cancel" class="btn btn-secondary" style="width: 20%">Cancel</button>
}
}
#if (ViewBag.Status == "TASK_CREATED")
{
<p> Good Job ! You requested a new password! </p>
}
</div>
So what I am trying to accomplish is this: First they go to that page, fill up their password request form and they submit, so now it is a POST. So on a POST I go create some stuff in DB for them using that this.CreateTask(fpModel); in the code. But After then I want the page to be reloaded and show something new like "Success! We submitted your request", I will add an OK button later there too but currently something like this:
#if (ViewBag.Status == "TASK_CREATED")
{
<p> Good Job ! You requested a new password! </p>
ALSO A BUTTON, Will Add Later
}
But this doesn't work, after they Submit, it reloads the page with a "GET" request and thus showing the form again. I want them to now see the other part of the page that was success message.

RedirectToAction method returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action
So, modify return RedirectToAction("ForgotPassword"); to
return View(fpModel);

Related

How to retain search results when clicking browser's back button - ASP.NET Core MVC

I am creating an ASP.NET Core 3 MVC application that has a Customers tab in addition to the Home tab. On the Customers tab there is an input box where the user adds a search criterion (number of days) and a Search button. When the button is clicked then a list of Customer Ids is shown underneath (using jQuery and a Partial View). When the user clicks on a customer Id then the customer information is shown in a different page. However when I click on the browser's back button or on the 'Customers' tab then the criterion added and the search results disappear.
I have tried using the ResponseCache attribute to retain the search results but I could not make it work. I have also tried using the Cache Tag Helper but again was not successful. Anyone can help?
CustomersController
public class CustomersController : Controller
{
private readonly DbContext _context;
public CustomersController(DbContext context)
{
_context= context;
}
public IActionResult Index()
{
return View();
}
public IActionResult DisplayCustomerIdList(string searchText)
{
List<CustomerDetailViewModel> customers = _context.GetAll().ToList();
CustomerIndexViewModel model = new CustomerIndexViewModel()
{
Customers = customers
};
return PartialView("_CustomerIdListView", model);
}
public IActionResult Detail(decimal? Id)
{
Customer customer = _context.GetCustomerById(Id);
CustomerDetailViewModel model = new CustomerDetailViewModel(customer);
return View(model);
}
}
Index.cshtml
#{
ViewData["Title"] = "Customers Page";
}
#section Scripts {
<script type="text/javascript" src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
var url = '#Url.Action("DisplayCustomerIdList", "Customers")';
$('#search').click(function () {
var keyWord = $('#NumberOfDays').val();
$('#searchResults').load(url, { searchText: keyWord });
return false;
})
</script>
}
<body>
<div class="input-group mb-3 w-50">
<input type="text" class="form-control mr-2" placeholder="Number of days" autocomplete="off" id="NumberOfDays">
<button id="search" class="btn btn-outline-info mb-2">Search</button>
</div>
<div id="searchResults"></div>
</body>
_CustomerIdListView.cshtml
#model MyProject.Models.CustomerIndexViewModel
<div class="card border-info mb-3 shadow" style="width:220px; height: 625px; overflow-y: scroll;">
<div class="card-header">Customer Ids</div>
<div class="list-group">
#foreach (CustomerDetailViewModel customerdetails in Model.Customers)
{
<a asp-controller="Customers" asp-action="Detail" asp-route-id="#customerdetails.CustomerId" class="list-group-item list-group-item-action">
#customerdetails.CustomerId
</a>
}
</div>
</div>
Detail.cshtml
#model MyProject.Models.CustomerDetailViewModel
<h3>Customer Information</h3>
<ul>
<li>#Model.CustomerId</li>
<li>#Model.FullName</li>
</ul>
Do the search via a GET request (rather than post). That way, the actual URL the user is sent to includes the query.
<form action="/foo" method="get">
I have figured out why this was not working and thought to add it here in case someone else has the same issue.
It turns out that the jQuery .load() method creates a POST request when the input parameter is an object (and a GET request when it is a String). So, because the ResponseCache attribute does not work with POST requests, the caching was not working.

Passing selected button value from view to controller

I would like to pass the selected button value to the controller. See my code below.
In my controller I am passing through the ProductId which I will then use to set up my product value inside my controller.
Controller:
public ActionResult PlaceOrder(int ProductId, string OrderType)
{
// Do something
}
Inside my view I have a foreach loop which will create radio like buttons and I have also got a hiddenfor(SelectedProductId)
View:
<div class="panel panel-primary">
<div class="panel-heading">Panel Name</div>
<div class="panel-body">
<div class="row">
<div class="form-group">
#Html.HiddenFor(m => m.SelectedProductId)
#if (Model.Products != null && Model.Products.Count > 0)
{
<div class="btn-group" data-toggle="buttons">
#foreach (var product in Model.Products)
{
<label class="btn btn-default productButton">
<div class="labelProduct">#Product.Name</div>
<input type="radio" name="ProductGMX" id="#("product" + #product.Id)" autocomplete="off" checked data-id="#product.Id">
</label>
}
</div>
I will want to pass the Product Id in the ActionLink which will then pass it to the controller but I am not sure how this can be achieved
Button Click:
#Html.ActionLink("Order with standard delivery", "PlaceOrder", "Standard", new { ProductId = ?, OrderType = "Standard delivery" }, new { area = "Standard" })
#Html.ActionLink("Order with Next day Delivery", "PlaceOrder", "Elevated", new { ProductId = ?, OrderType = "NextDay" }, new { area = "Elevated", })
You either need to use JavaScript to update the ActionLink's url whenever the product changes, using the data-id from the radio button.
Or
Use submit buttons instead of ActionLinks, and set the value on the radio button to the product id. You'll need to put some logic in your controller to handle the two different buttons.
Those aren't buttons. They're links, which don't participate in the form submission.
Use real buttons, i.e. <button></button> and give them a name. Then you can see which was clicked by inspecting the Request object:
<button name="_StandardDelivery">Order with standard delivery</button>
Then in your action:
if (Request["_StandardDelivery"] != null) {
// user chose standard delivery
}

FileUpload: keep filenames when showing validation errors

I have the following file-upload code in C# using Asp.Net MVC. The problem is that when validation errors are shown, all the files chosen by the user are lost (input boxes are cleared). Is it possible to keep the input filenames in their original ordering without using javascript? What's the simplest approach?
Controller code
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
{
//check for errors: if errors, ModelState.AddModelError(...);
if (!ModelState.IsValid) {
return View(files);
}
else {
//..........
}
}
View snippet
#using (Html.BeginForm("Index", "Uploader", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<input type="file" name="files" id="file1" />
#Html.ValidationMessage("1")
</div>
<div class="form-group">
<input type="file" name="files" id="file2" />
#Html.ValidationMessage("2")
</div>
//and 2 more file input fields
<div>
<input type="submit" value="Upload Files" class="btn btn-success btn-lg" />
</div>
}
In postback you can not retain same local path which will remain in type="file".
to do this think there are two way
1) in your current code if you find any file attached then save on server and maintain some flag in hidden field and hide/show your file control with text box( having only filename)
and send it back to browser. Then on next valid submit take that file name that you have already saved. and do your process.
2) On submit of form, copy(DOM copy) all your html control(file ,textbox, hidenfield ect..) in one iframe and submit that iframe.
In case anybody is still in search of a possibility, here is the work around that worked for me. I'm using MVC5. The idea is to use a session variable. I got the idea from ASP.Net Form.
My Model/ViewModel (only relevant properties):
public partial class emp_leaves
{
public string fileNameOrig { get; set; }
public byte[] fileContent { get; set; }
public HttpPostedFileBase uploadFile { get; set; }
}
In my controller (HttpPost):
//Check
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(emp_leaves emp_leaves)
{
if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
{
emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);
Session["emp_leaves.uploadFile"] = emp_leaves.uploadFile; //saving the file in session variable here
}
else if (Session["emp_leaves.uploadFile"] != null)
{//if re-submitting after a failed validation you will reach here.
emp_leaves.uploadFile = (HttpPostedFileBase)Session["emp_leaves.uploadFile"];
if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
{
emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
emp_leaves.uploadFile.InputStream.Position = 0;
emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);
}
}
//code to save follows here...
}
Finally within my edit view: here, i am conditionally showing the file upload control.
< script type = "text/javascript" >
$("#removefile").on("click", function(e) {
if (!confirm('Delete File?')) {
e.preventDefault();
return false;
}
$('#fileNameOrig').val('');
//toggle visibility for concerned div
$('#downloadlrfdiv').hide();
$('#uploadlrfdiv').show();
return false;
}); <
/script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
#model PPMSWEB.Models.emp_leaves #{ HttpPostedFileBase uploadFileSession = Session["emp_leaves.uploadFile"] == null ? null : (HttpPostedFileBase)Session["emp_leaves.uploadFile"]; } #using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data"
})) { #Html.AntiForgeryToken()
<div class="row">
#*irrelevant content removed*#
<div id="downloadlrfdiv" #((!String.IsNullOrEmpty(Model.fileNameOrig) && (Model.uploadFile==n ull || uploadFileSession !=null)) ? "" : "style=display:none;")>
<label>Attachment</label>
<span>
<strong>
<a id="downloadlrf" href="#(uploadFileSession != null? "" : Url.Action("DownloadLRF", "emp_leaves", new { empLeaveId = Model.ID }))" class="text-primary ui-button-text-icon-primary" title="Download attached file">
#Model.fileNameOrig
</a>
</strong>
#if (isEditable && !Model.readonlyMode)
{
#Html.Raw("&nbsp");
<a id="removefile" class="btn text-danger lead">
<strong title="Delete File" class="glyphicon glyphicon-minus-sign"> </strong>
</a>
}
</span>
</div>
<div id="uploadlrfdiv" #(!(!String.IsNullOrEmpty(Model.fileNameOrig) && Model.uploadFile==n ull) && !Model.readonlyMode ? "" : "style=display:none;")>
<label>Upload File</label> #Html.TextBoxFor(model => model.uploadFile, new { #type = "file", #class = "btn btn-default", #title = "Upload file (max 300 KB)" }) #Html.ValidationMessageFor(x => x.uploadFile)
</div>
</div>
}

Text response displayed in a view's unbound label

I have a view that contains a submit button. When that submit button is clicked some code runs a process. I want to return a text message to a label on the view that will let the user know that their submission was successful or there was an error.
I've searched around and I have found many examples about labels but I haven't come across one that shows me how to do what I want.
My Controller:
public ActionResult Import()
{
//Some code that runs a process
//Need to know what code will return "Import was Successful" or "Erroring Importing"
return RedirectToAction("Import")
}
My View:
#{
ViewBag.Title = "Import";
}
<h2>Import</h2>
#using (Html.BeginForm("Importexcel", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr><td>Import Files</td><td><input type="file" id="FileUpload1" name="FileUpload1" /></td></tr>
<tr><td></td><td><input type="submit" id="Submit" name="Submit" value="Submit" /></td></tr>
**<tr><td>#Html.Label(returned results)</td></tr>** // Need to know how to do this
</table>
}
In your view:
#using (Html.BeginForm("Importexcel", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr><td>Import Files</td><td><input type="file" id="FileUpload1" name="FileUpload1" /></td></tr>
<tr><td></td><td><input type="submit" id="Submit" name="Submit" value="Submit" /></td></tr>
**<tr><td>#Html.Label(returned results)</td></tr>** // Need to know how to do this
</table>
#ViewBag.Message
}
In your controller:
[HttpPost]
public ActionResult Import(){
//Some code that runs a process
//Need to know what code will return "Import was Successful" or "Erroring Importing"
if(something){
ViewBag.Message = "Import Failed";
}
else
{
ViewBag.Message = "Import Successful";
}
return View();
}
Try and give that a shot.
You can always pass either a key to a look-up table of messages or the message itself via the query string. Here's an example:
Controller Action
public ActionResult Import(string message = null)
{
// Detect presence of message (i.e. !String.IsNullOrWhiteSpace(message)) and show it.
// Additional logic after this...
return RedirectToAction("Import", "YourControllerNameHere", new { message = "Your message here..." });
}
Then it's just a matter of wiring up your model or ViewModel in the Import view so it displays the appropriate message.

Uploading file through an action of a controller - two forms in one view

The problem is I have two forms in the same view. The controller is Users and I have two actions for the two forms: Edit and UploadPhoto.
I have been using the Edit portion for awhile and it's working. Now I want to allow users to upload their photo on the same page. There is a separate button for saving the user information and another for saving the picture.
The page model is #model sportingbiz.Models.ViewModels.UserEditViewModel
The second form
<fieldset title="Upload photo">
<legend>Change Profile Picture</legend>
<div>
#using(Html.BeginForm("UploadPhoto", "Users", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr>
<td>
<img src="#Url.Content("~/Content/images/noprofilepic.jpg")"
</td>
<td>
<input type="file" name="photoFile" />
<br /><br />
<input type="submit" value="Upload" />
</td>
</tr>
</table>
}
</div>
</fieldset>
Controller
public class UsersController : Controller
{
UsersRepository usersRepo = new UsersRepository();
[Authorize]
[HttpPost]
public void UploadPhoto(HttpPostedFile photoFile)
{
if (photoFile == null)
{
return;
}
if (photoFile.ContentLength <= 0)
{
return;
}
string filename = Path.GetFileName(photoFile.FileName);
if (Path.GetExtension(filename) == "")
return;
}
}
When I click upload my page navigates to http://localhost/mvcapp/Users/UploadPhoto with a blank screen. What I want is to return to the Edit page and probably show errors to the user that file was not uploaded. Features like ModelState.IsValid or ModelState.AddModelError
There is also no option of returning the same view e.g. return View(model)
Am I doing this wrong way? Some suggestion is to use Ajax uploader or Uploader. Am also thinking of separating the forms putting the Upload portion in its own view. Can PartialView help in this respect
[Authorize]
[HttpPost]
public void UploadPhoto(HttpPostedFile photoFile)
{
An Action with a void return is very strange. Make it return an ActionResult and determine on each branch where you want to go.
[Authorize]
[HttpPost]
public void UploadPhoto(HttpPostedFile photoFile)
{
if (photoFile == null)
{
return RedirectToAction("ErrorPage");
}
...
var viewModel = ...; // but your've lost the Edit part
return View(viewModel);
}
try to validate the using jquery like below, which will make sure that user are force to select some file...
lets us assume you a view like below
<input type="file" name="Document" id="imgFile" style="height:25px;"/>
<input type="submit" value="Submit" id="btnSubmit"/>
below is the jquery to validate
$('#imgFile').change(function() {
if ( ! $('#imgFile').val() ) {
alert('Chose a file!');
return false;
}
});
or you can do this same on the Button click like below
$('#btnSubmit').click(function() {
if ( ! $('#imgFile').val() ) {
alert('Chose a file!');
return false;
}
});
hope this might help you...
you uplode your image like that
public void UploadPhoto(HttpPostedFile photoFile)
{}
i think you should little bit change you code like this
public void UploadPhoto(HttpPostedFileBase photoFile)
{}

Categories

Resources