I hope there is a simple answer to this problem which I'm just not seeing.
I am developing an asp.net MVC 4 application.
Basically I want to allow the user to edit the contents of a previous entry(newsArticle) they would have made (sounds easy)
This entry consist's of a date, two blocks of text, (Title and body) an img.
These entries correspond to properties of a model, which also include, ID , img name and status.
I have successfully manged to enter, save and retrieve all the data when needed.
However when I run the program and try and edit a previous entry it only saves the Title and the body text and removes the values that were there, for date, img, img name and status.
Is this related to the textboxes used in the view ? Why are those values kept and the others not.
Just to point out when I run and select an entry all the data is shown correctly but when I click save values are removed (date, img name , status)
Any thoughts.....
Controller....
public ActionResult Edit(int id)
{
String encodedBody;
NewsArticle newsArticle = _newsArticle.Get(id);
encodedBody = HttpUtility.HtmlDecode(newsArticle.Body.ToString());
newsArticle.Body = encodedBody;
return View(newsArticle);
}
// POST: /Article/Edit/5
[HttpPost]
public ActionResult Edit(NewsArticle newsArticle)
{
try
{
if (ModelState.IsValid)
{
db.Entry(newsArticle).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (System.Data.DataException)
{
//Log th error(add a variable name after DataExpection)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(newsArticle);
}
View.......
#using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data", #class = "form-horizontal", id = "newsEditForm"}))
{
#Html.ValidationSummary()
#Html.HiddenFor(model => model.ID)
<div class="control-group">
<label class="control-label">Posted on :</label>
<div class="controls">
<span class="text">#Model.DateCreated.Value.ToShortDateString()</span>
#*#Html.LabelFor(n => n.DateCreated)*#
</div>
</div>
<div class="control-group">
<label class="control-label">#Html.LabelFor(n => n.Title)</label>
<div class="controls">
#Html.TextBoxFor(n => n.Title, new { #class = "span4 m-wrap", rows = 1})
</div>
</div>
<div class="control-group">
<label class="control-label">#Html.LabelFor(n => n.Body)</label>
<div class="controls">
#Html.TextAreaFor(n => n.Body, new { #class = "span12 ckeditor m-wrap", rows = 4 })
</div>
</div>
<div class="control-group">
<label class="control-label">Selected Image </label>
<label class="controls">#Html.DisplayTextFor(model => model.ImageName)</label>
#* <div class="span4 blog-img blog-tag-data">
<div class="editor-field">
<input type="file" name="Article" id="ArticleImage"/>
</div>
</div>*#
</div>
<div class="form-actions">
<button type="submit" class="btn green" id="submitnews"><i class="icon-ok"></i>Submit</button>
#Html.ActionLink("Cancel", "ArticleList", "Admin", null, new { #class = "btn blue"})
#*<button type="button" class="btn blue" onclick="location.href='ArticleList','Admin'">Cancel</button>*#
</div>
}
Your problem is you're saving the newsArticle that's posted back directly to the DB. The posted newsArticle contains a null entry for the image, date, img name and status because those fields don't exist in your form. You really shouldn't pass domain models back to your edit for this and other security reasons. However, this is a quick fix to your issue
[HttpPost]
public ActionResult Edit(NewsArticle newsArticle)
{
try
{
if (ModelState.IsValid)
{
NewsArticle savedArticle= _newsArticle.Get(newsArticle.Id);
savedArticle.Body = newsArticle.Body
savedArticle.Title = newsArticle.Title
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (System.Data.DataException)
{
//Log th error(add a variable name after DataExpection)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(newsArticle);
}
Related
I'm currently learning RazorPages in codeacademy.com.
I did everything that was shown in the video Tutorial, and unfortunately is doesn't work:
The task for the project:
"Data Delete
The current project has a button on the Index page list that deletes the current Continent or Country. The button will be modified to call a discrete Delete.cshtml page. This page will display the current record for review and provide a Delete button. Once the deletion occurs, the user is redirected back to the list so they get visual feedback of a successful task.
The code and markup are easily copied from the existing Detail.cshtml page. After copying that page, we add a delete button and copy the necessary statements from the OnPostAsync() method in the Index.cshtml.cs page."
The Delete page was created. The problem is:
When I press button Delete on the Delete page I have redirection to this link in browser:
https://localhost/Continents/Delete/SA?Continent.ID=SA
Actually no Delete happends
No redirection
What mistakes maybe here?
The code Delete.cshtml:
#page "{id}"
#model DeleteModel
#{
ViewData["Title"] = "Continent Delete";
}
<div class="jumbotron p-3">
<div class="d-flex align-items-center">
<h1 class="display-4 flex-grow-1">
Continent Delete
</h1>
<a class="btn btn-primary btn-sm" asp-page="./Index">
Back to List
</a>
</div>
</div>
[enter image description here](https://i.stack.imgur.com/tFnrX.jpg)
<div class="d-flex">
<div class="p-2 bg-primary text-white text-right" style="flex:0 0 15%">
#Html.DisplayNameFor(model => model.Continent.ID)
</div>
<div class="p-2 border-top border-right border-bottom border-primary" style="flex:1 0 auto">
#Html.DisplayFor(model => model.Continent.ID)
</div>
</div>
<div class="d-flex">
<div class="p-2 bg-primary text-white text-right" style="flex:0 0 15%">
#Html.DisplayNameFor(model => model.Continent.Name)
</div>
<div class="p-2 border-right border-bottom border-primary" style="flex:1 0 auto">
#Html.DisplayFor(model => model.Continent.Name)
</div>
</div>
<form metod="post" class="d-flex flex-row-reverse mt-3">
<input type="hidden" asp-for="Continent.ID"/>
<input type="submit" value="Delete" class="btn btn-danger btn-sm"/>
</form>
Delete.cshtml.cs:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorCountry.Models;
using RazorCountry.Data;
namespace RazorCountry.Pages.Continents
{
public class DeleteModel : PageModel
{
private readonly CountryContext _context;
public DeleteModel(CountryContext context)
{
_context = context;
}
public Continent Continent { get; set; }
public async Task<IActionResult> OnGetAsync(string id)
{
Continent = await _context.Continents
.Include(c => c.Countries)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Continent == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(string id)
{
if (id == null)
{
return NotFound();
}
// Find the continent
Continent Continent = await _context.Continents.FindAsync(id);
//Delete the continent
if (Continent != null)
{
_context.Continents.Remove(Continent);
}
//Persist Changes
await _context.SaveChangesAsync();
//Redirect to the user
return RedirectToPage("./Index");
}
}
}
Model Continent.cs:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace RazorCountry.Models
{
public class Continent
{
[Required, StringLength(2, MinimumLength = 2), Display(Name = "Code")]
[RegularExpression(#"[A-Z]+", ErrorMessage = "Only upper case characters are allowed.")]
public string ID { get; set; }
[Required]
public string Name { get; set; }
public ICollection<Country> Countries { get; set; }
}
}
Try to understand how RazorPages functional works, try to fix mistake in a correct way
The reason that no error occurs is because your OnPostAsync method is not being hit. You have mis-spelled the method attribute in your form (you have metod) so submitting it generates the default GET request and your OnGetAsync handler is executed instead.
Once you have corrected that error, you still have an unresolved issue.
Your OnPostAsync handler expects a parameter named id. The hidden field tag helper in your form generates a parameter with a name of Continent.ID. Consequently the value will not be bound to the id parameter. The simplest solution is to do away with the tag helper in the form a the bottom of your Delete page and replace it with a plain HTML input with a name attribute:
<form method="post" class="d-flex flex-row-reverse mt-3">
<input type="hidden" name="id" value="#Continent.ID"/>
<input type="submit" value="Delete" class="btn btn-danger btn-sm"/>
</form>
You can read more about model binding in general here: https://www.learnrazorpages.com/razor-pages/model-binding
Thanks to #MikeBrind finally, I changed the code
<form method="post" class="d-flex flex-row-reverse mt-3">
<input type="hidden" name="id" value="#Model.Continent.ID"/>
<input type="submit" value="Delete" class="btn btn-danger btn-sm"/>
</form>
And it works!
So I am trying to make a form on a asp.net website where a user loads a page in this case called Software once this page loads it checks if two variables are empty and if so it returns the Software View which then allows a user to fill in information for those said variables. The problem is when the user puts in the information the submit button doesn't actually submit the changes. If it did then the IF statement should no longer be valid and would be ignored so the page would be redirected to the PostSoftware page. My assumption is I might need some sort of javascript check for when submit button is clicked to do something but I am unsure.
Software Frontend:
<div class="jumbotron" style="background-color:#D1D3D4;">
<img src="/Images/TECH_BARlogoBLACK.png" style="width:1000px;height:200px;" class="animate__heartBeat">
<p class="lead" span style="background-color: #FFFF00;">Software Request</p>
<br>
<p class="text-left"><b>Enter Computer Name:</b></p>
#Html.TextBoxFor(x => x.computerName, new { id = "computerName", name = "computerName"})
<p style="font-size: 14px; text-align: left;"><b>To obtain your Computer Name: </b>Click Start (bottom left windows icon) and type in System Information, On the 5th line, your Computer Name is listed as the System Name <br></p>
<p class="text-left" style="width: 1000px;"><b>Business Justification:</b> <br> #Html.TextAreaFor(x => x.businessJustification, new { id = "businessJustification", name = "businessJustification", #cols = 40, #rows = 3,#style="width: 1000px;"}) </p>
<label for="Software">Select the Software(s) you need!</label>
<br>
<!-- NOTICE FROM HERE DOWN -->
#Html.DropDownListFor(model => #Model.selectedSoftwareList, new MultiSelectList(Model.softwareList, "Text", #Model.selectedSoftwareList),
new
{
id = "_SelectedSoftwareList",
#class = "form-control multiselect-dropdown",
multiple = "true",
style = "width:200px;height:300px;",
data_placeholder = "Select Software"
})
<br>
<br><button type="submit" class="btn-primary btn-lg" style="font-size: 20px">Submit</button>
</div>
Controller
public ActionResult Software(TicketModel model)
{
ViewBag.Message = "Request software through the Tech Bar.";
var snow = new clsServNowAuth();
var tbl = clsServNowAuth.SoftwareTable();
var tbl2 = clsServNowAuth.SoftwareTableID();
tbl.Wait();
tbl2.Wait();
model.softwareList = tbl.Result;
model.softwareListID = tbl2.Result;
if (String.IsNullOrEmpty(model.computerName) || String.IsNullOrEmpty(model.businessJustification))
{
// TODO
return View(model);
}
return RedirectToAction("PostSoftware", model);
}
[HttpPost]
public ActionResult PostSoftware(TicketModel model)
{
if (String.IsNullOrEmpty(model.computerName) || String.IsNullOrEmpty(model.businessJustification))
{
// TODO
return View(model);
}
// get user info from AD currently ZID and Email
List<string> lstUserInfo = clsADInterface.GetInfo(model);
var test = model.selectedSoftwareList;
// submit software request
clsServNowAuth.SoftwareRequest(lstUserInfo, model.computerName, model.businessJustification, model.softwareList, model.softwareListID);
return View(model);
}
}```
I was missing the form creation for the submit button
#using (Html.BeginForm(FormMethod.Post))
{ }
In my view model, I have a list of objects. I iterate these objects, and create controls for each of them. In the situation below, I want to show people a textbox and a button for each object. When the user clicks the button, a post is made, and I can save the data in my controller.
In the UI, a user can change the form they want, and click save.
My problem is the model is null when it's posted to the controller..
My Razor code:
using (Html.BeginForm())
{
foreach (var contributor in Model.Contributor)
{
#Html.HiddenFor(model => contributor.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => contributor.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => contributor.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
}
My view model code
public class ProfileModel
{
public string Message { get; set; }
public List<PublisherModel> Publisher { get; set; }
public List<ContributorModel> Contributor { get; set; }
public ContributorModel NewContributor { get; set; }
}
My controller code
[HttpPost]
public ActionResult Mine(ProfileModel model, string newuser)
{
//
}
How to fix it?
I guess I have to expand my view model with a way to store the changes in some way. But I really can't see how.
Right now all the properties in the ProfileModel is null when it reaches the controller.
Any ideas?
Basically the problem is that default model binder is unable to bind collection items correctly in foreach loop. In other words, it names the element incorrectly and that's why the collection displays as null in parameters.
I'm sure there are all kinds of different workarounds, helpers and stuff but I'm not familiar with those, so I just use for loop instead of foreach, this way the elements are named correctly.
Try this:
#for (int i = 0; i < Model.Contributor.Count(); i++)
{
#Html.HiddenFor(model => Model.Contributor[i].Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.Contributor[i].FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.Contributor[i].FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
I suggest you to use a debugging tool to see if elements have correct name attribute, in your case they should look like Contributor[0].Id, Contributor[0].FirstName etc.
You can use PartialView for Contributor object.
PartialView:
#model Contributor
using (Html.BeginForm("ContributorUpdate", "YourController"))
{
#Html.HiddenFor(model => Model.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
View will be:
#foreach (var contributor in Model.Contributor)
{
#{Html.RenderPartial("Conributor", contributor);}
}
And controller code:
[HttpPost]
public ActionResult Mine(Conributor conributor, string newuser)
{
//
}
I am trying to pass two parameters through a #Url.Action for a submit button. However, the values are not being passed.
I have index view with a dropdown field and a Save button. What I would like for the user to be able to do is select a dropdown value and Save the value. However, no matter how I access my function the values are null. I have tried passing in parameters and binding the data.
Currently my view looks like -
<h3>Burn Conditions</h3>
<div class="form-group">
#Html.LabelFor(model => model.ConditionsReasonsID, "Condition", new { #class = "control-label col-md-2"})
<div class="col-md-10">
#Html.DropDownList("ConditionsReasonsID", String.Empty)
#ViewBag.conditionsReasons
#Html.ValidationMessageFor(model => model.ConditionsReasonsID)
<a href= "#Html.Raw(#Url.Action("CreateCondition", "RequestedBurns", new { requestedBurnsID = ViewBag.RequestedBurnsID, conditionsReasonsID = Model.ConditionsReasonsID }))">
<input type="submit" value="Save Condition" />
</a>
</div>
</div>
in my controller I have
public ActionResult CreateCondition(int requestedBurnsID, int conditionsReasonsID)
{
BurnDecision burnDecision = new BurnDecision();
burnDecision.RequestedBurnsID = requestedBurnsID;
burnDecision.ConditionsReasonsID = conditionsReasonsID;
//Find BurnSiteID
RequestedBurn requestedBurn = db.RequestedBurns.Find(burnDecision.RequestedBurnsID);
ViewBag.burnSitesID = requestedBurn.BurnSitesID;
db.BurnDecisions.Add(burnDecision);
db.SaveChanges();
return RedirectToAction("Index", "RequestedBurns", new { burnSitesID = ViewBag.burnSitesID, requestedBurnsID = burnDecision.RequestedBurnsID });
}
My project is Room-reservation service .I have View :
#model Client
#using (Html.BeginForm("SaveUserDB", "Booking", FormMethod.Post))
{
<div class="editor-label">
#Html.Label("Surname")
#Html.TextBoxFor(model => model.Surname)
</div>
<div class="editor-label">
#Html.Label("Name")
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.Label("Patronymic")
#Html.TextBoxFor(model => model.Patronymic)
</div>
<input type="submit" id="submit" value="Reservation" />
And I have controller for this View:
[HttpPost]
public ActionResult SaveUserDB(Client client)
{
if (ModelState.IsValid)
{
using (db)
{
db.Client.Add(client);
db.SaveChanges();
return RedirectToAction("Thankyou");
}
}
return View(client);
}
This controller save data client to database table Client. But I need also create record in second table Reservation, which takes parameters: Datetime.Now , and Client.Id. Parameter Client Id in database is autoincrement, but doesn't display in the View.
Well, if this is how you add a record to the Client table:
db.Client.Add(client);
Then why not use that same exact approach to add a record to the Reservation table? Something like this:
var reservation = new Reservation
{
ClientID = client.ID,
SomeOtherColumn = DateTime.Now
};
db.Reservation.Add(reservation);
(Note: This is based on speculation of what your Reservation object/table might look like based on your description. But the concept is the same. Create an instance of a reservation object and add it to the data context.)