Partial View displays wrong data after update (asp.net MVC5) - asp.net-mvc-5

There are a few similar questions on here that I have checked but none seem to answer my issue, so here's hoping someone can help me out.
I have a form in a view, and a partial view I'm using like a subform. The Partial view is used to display an iList of items. (Screenshot below to show how this appears).
In the partial view, each item has a checkbox which the user can check to delete it. If I check the checkbox for the first item, the first item is removed from the list in code, but when the model is passed back to the View, the wrong item (the checked item) is the one that comes back.
So in the example below, if I check the first item (No Answer Delay = 18) and submit, that same item stays on the page whilst the other one (No Answer Delay = 10) disappears. If I then reload all the data, the correct item (No Answer Delay = 10) appears.
I have checked in the method that the correct data is being passed back, but the wrong item remains on the page. If I then refresh the page, the correct item appears. Note, the method has been sanitised a bit but the correct item does get removed them the database.
One other thing to note, this is a plugin to a 3rd party product so I cannot run it unless I publish to the other product, making debugging tricky.
The code for the main view is
#using(Html.BeginForm("SaveCallFeatures", "CallFeatures", FormMethod.Post, new { id = "CallFeatures", name = "CallFeatures" }))
{
#Html.AntiForgeryToken()
<div>
<h2>Call Features</h2>
<div class="form-panel">
<h4>Telephone Call Features</h4>
<div>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.phoneNumber, htmlAttributes: new { #class = "label" })
#Html.EditorFor(model => model.phoneNumber, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
#Html.ValidationMessageFor(model => model.phoneNumber, "", new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.password, htmlAttributes: new { #class = "label" })
#Html.EditorFor(model => model.password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.password, "", new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.hideOutgoingCallerID, htmlAttributes: new { #class = "label" })
#Html.CheckBoxFor(model => model.hideOutgoingCallerID, new { htmlAttributes = new { #class = "form-control" } })
</div>
<div>
#Html.LabelFor(model => model.callWaiting, htmlAttributes: new { #class = "label" })
#Html.CheckBoxFor(model => model.callWaiting, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div id="ForwardRules">
#Html.Partial("_ForwardingRules")
</div>
</div> //form
#Html.TextArea("Test")
<div id="form-buttons" class="col-md-offset-4 col-md-6">
<input type="button" value="Save" id="save-button" class="btn btn-primary" />
</div>
<script type="text/javascript">
$("#update-button").on('click', function () {
GetFwdRules();
});
</script>
function GetFwdRules() {
$.ajax
({
url: '#Url.Action("GetFwdRules", "CallFeatures", new { boid = Model.CompanyId })',
method: 'GET',
data: $("#CallFeatures").serialize(),
cache: false,
success: function (returnData) {
$("#ForwardRules").html(returnData);
$("#Test").html(returnData);
alert('GetFwdRules');
},
failure: function () {
alert('GetFwdRules Failure');
}
});
}
The code the the Partial View is
#model XXXXX.Models.CallFeaturesModel
<div class="form-panel">
<h4>Active Forwarding Rules</h4>
#for(int i = 0; i < Model.FwdRules.Count; i++)
{
<div>
#Html.HiddenFor(model => Model.FwdRules[i].ForwardingRuleID)
</div>
<div>
#Html.LabelFor(model => Model.FwdRules[i].Condition)
#Html.TextBoxFor(model => Model.FwdRules[i].Condition, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
</div>
<div>
#Html.LabelFor(model => Model.FwdRules[i].Destination)
#Html.TextBoxFor(model => Model.FwdRules[i].Destination, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
</div>
<div>
#Html.LabelFor(model => Model.FwdRules[i].NoAnswerDelay)
#Html.TextBoxFor(model => Model.FwdRules[i].NoAnswerDelay)
#Html.DescriptionFor(model => model.FwdRules[i].NoAnswerDelay)
</div>
<div>
#Html.LabelFor(model => Model.FwdRules[i].ToDelete)
#Html.CheckBoxFor(model => Model.FwdRules[i].ToDelete)
</div>
<br />
}
This is the method
[HttpGet]
public ActionResult GetFwdRules(CallFeaturesModel CFModel)
{
// Refresh the list to include on those where the ToDelete variable == false (checkbox is unchecked)
CFModel.FwdRules = CFModel.FwdRules.Where(x => x.ToDelete == false).ToList();
return PartialView("_ForwardingRules", CFModel);
}
And this is the model
public class CallFeaturesModel : UIPluginBaseModel
{
[Display(Name = "Phone Number")]
public string phoneNumber { get; set; }
[Display(Name = "Password")]
public string password { get; set; }
[Display(Name = "Hide Outgoing Caller ID")]
public bool hideOutgoingCallerID { get; set; }
[Display(Name = "Call Waiting")]
public bool callWaiting { get; set; }
[Display(Name = "Message")]
public string Message { get; set; }
public IList<ActiveForwardRule> FwdRules { get; set; }
}
public class ActiveForwardRule
{
[Display(Name = "Rule ID")]
public string ForwardingRuleID { get; set; }
[Display(Name = "Condition")]
public string Condition { get; set; }
[Display(Name = "Destination")]
public string Destination { get; set; }
[Display(Name = "No Answer Delay", Description = " seconds (approx. 6 seconds for each ring cycle)")]
public int NoAnswerDelay { get; set; }
[Display(Name = "Delete")]
public bool ToDelete { get; set; }
}
Here's a screenshot of the example. Looks like I'm not allowed to embed an image yet.
Hoping someone can point out where I am going wrong.

When posting data and then re-displaying data in the same request, the ModelState will be populated with the data from the original post.
This can lead to situations where items that should have been deleted still show or a form being pre-filled when it should now be blank.
Adding:
ModelState.Clear()
before re-displaying the data will clear down the model state and prevent the tag helpers from populating themselves from the original post request

Related

Password and confirm password does not match

Password and confirm password does not match.
Even if you write the same password in both fields gives me error and says passwords does not match.
This is how Account looks like:
[Required]
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
And this is the register page:
<div class="form-group">
#Html.LabelFor(model => model.Password, new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.Password, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.Password, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ConfirmPassword, new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.ConfirmPassword, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.ConfirmPassword, "", new {#class = "text-danger"})
</div>
</div>
I searched on google and this forum , but I have not found anything that works.
Can someone help me ?
Here is the database:
http://i.stack.imgur.com/D86QV.png
You can try to register: http://ursaciucadrian.somee.com/Home/Register?Length=4
There was an old bug from 2013, Try to update all of the project's dependencies:
PM> update-package
After a lot of tutorials and searches I realized that the problem was from scaffolding.
I had to change
public ActionResult Register([Bind(Include = "AccountId,FullName,Username,Password,Email")] Account account)
{
if (ModelState.IsValid)
{
db.Accounts.Add(account);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(account);
}
in this:
public ActionResult Register(Account account)
{
if (ModelState.IsValid)
{
db.Accounts.Add(account);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(account);
}

How to add 2 user types for registration on asp.net mvc 5 identity

I some how managed to add first name,last name and email properties for registration for my open source booking system but I want 2 user types. the default user type should be clients and second type should be Hostlers. The options can be shown by dropdown or radio buttons. i would like know if it is ok to have 2 registration form? Will it create any conflicts? Or will it just be waste of time to code 2 forms for 2 similar job?
Should the user type be saved as user roles or should be done as user type and use role for only 3rd user type(website sole admin or owners registered internally)
my modification to default asp.net identity's ApplicationUser class
public class ApplicationUser : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
}
here is register.cshtml (showing only what i added)
// new properties
<div class="form-group">
#Html.LabelFor(m => m.FirstName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.LastName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.LastName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
here is regiViewModels.cs
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
// for first name
[Required]
[Display(Name = "First name")]
public string FirstName { get; set; }
//for last name
[Required]
[Display(Name = "Last name")]
public string LastName { get; set; }
//for email
[Required]
[StringLength(60, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 5)]
[Display(Name = "Email")]
public string Email { get; set; }
// Return a pre-poulated instance of AppliationUser:
public ApplicationUser GetUser()
{
var user = new ApplicationUser()
{
UserName = this.UserName,
FirstName = this.FirstName,
LastName = this.LastName,
Email = this.Email
};
return user;
}
}
AccountController.cs
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() {
UserName = model.UserName,
FirstName = model.FirstName,
LastName = model.LastName,
Email = model.Email
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
thank you in advance
Personally I would use "Roles" to define what access each has. This is what roles are for.
Think about it... what if one of your hoteliers also wants to book rooms as a user?
So roles might be "Can book rooms", "Can list hotels", etc. But ultimately you'll have to figure out how best to manage this. It's likely that you'll control which users are hoteliers via your admin account right?

Uploading and retrieving image and video url in ASP.NET MVC4 Internet Application

I have the following model in my MVC 4 Internet Application
public class EventModel
{
public int EventId { get; set;}
public string EventTitle { get; set;}
public string ImageUrl { get; set;}
public string VideoUrl { get;set}
}
Here's the Create.cshtml code for the Image and Video
#using(Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div>
#Html.LabelFor(model =>model.ImageUrl)
</div>
<div>
#Html.EditorFor(model =>model.ImageUrl)
#Html.ValidationMessageFor(model =>model.ImageUrl)
</div>
<div>
#Html.LabelFor(model =>model.VideoUrl)
</div>
<div>
#Html.EditorFor(model =>model.VideoUrl)
#Html.ValidationMessageFor(model =>Video.ImageUrl)
</div>
}
This is my controller code for the post method on create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(EventModel eventmodel)
{
if (ModelState.IsValid)
{
_db.EventModels.Add(eventmodel);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(eventmodel);
}
How can I make the create page display buttons to upload image from the users computer. How do i modify my controller action to store the paths on the database and then how do I retrieve the image on the index.cshtml page.
For file uploading try this:
In controller:
[HttpPost]
public ActionResult Create(EventModel eventmodel, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
var filename = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads/Photo/"), filename);
file.SaveAs(path);
tyre.Url = filename;
_db.EventModels.AddObject(eventmodel);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(eventmodel);
}
And View:
<div>
Image
<input type="file" name="file" id="file" />
#Html.HiddenFor( model => model.ImageUrl)
#Html.ValidationMessageFor( model => model.Url )
</div>

Orchard Simple Widget Not Saving database values

I've been having a little problem creating a simple widget. All I want to do is create a widget that has the a MediaPickerField and 2 int and 2 string fields that are stored in the database using a part. Everything works as advertised. ContentRecord is created and the image field is properly stored, except that the four fields are not being stored. There are no error in the logs and cannot seem to see why this is happening. Has anyone come across this before? Thank you.
Migration.cs
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SchoenNavButtonPartRecord",
table => table.ContentPartRecord()
.Column<string>("Url", col=> col.WithLength(2140))
.Column<string>("Text")
.Column<int>("ButtonWidth")
.Column<int>("ButtonHeight"));
ContentDefinitionManager.AlterTypeDefinition("SchoenNavButton", builder =>
builder.WithPart("CommonPart")
.WithPart("SchoenNavButtonPart")
.WithPart("TitlePart")
.Creatable());
ContentDefinitionManager.AlterPartDefinition("SchoenNavButtonPart", builder =>
builder.WithField("ButtonImage", field=>
field.OfType("MediaPickerField")
.WithDisplayName("Button Image")
.WithSetting("Hint", "Select Image for Button")));
return 2;
}
PartRecord
public class SchoenNavButtonPartRecord : ContentPartRecord
{
public virtual string Url { get; set; }
public virtual string Text { get; set; }
public virtual int ButtonWidth { get; set; }
public virtual int ButtonHeight { get; set; }
}
Part
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url { get; set; }
public string Text { get; set; }
[DefaultValue(296)]
public int ButtonWidth { get; set; }
[DefaultValue(188)]
public int ButtonHeight { get; set; }
}
Handler
public class SchoenNavButtonHandler : ContentHandler
{
public SchoenNavButtonHandler(IRepository<SchoenNavButtonPartRecord> buttonImageLinkRepository)
{
Filters.Add(StorageFilter.For(buttonImageLinkRepository));
}
}
Driver
public class SchoenNavButtonPartDriver : ContentPartDriver<SchoenNavButtonPart>
{
public SchoenNavButtonPartDriver()
{
}
protected override string Prefix
{
get
{
return "SchoenNavButton";
}
}
protected override DriverResult Display(SchoenNavButtonPart part, string displayType, dynamic shapeHelper)
{
var fields = ((ContentPart) part).Fields.OfType<MediaPickerField>();
MediaPickerField mediaPickerField = null;
if(fields.Any())
{
mediaPickerField = fields.ElementAt(0);
}
return ContentShape("Parts_SchoenNavButton",
() => shapeHelper.Parts_SchoenNavButton(
SchoenNavButtonPart: part,
ImageUrl: mediaPickerField == null ? "#" : mediaPickerField.Url
));
}
protected override DriverResult Editor(SchoenNavButtonPart part, dynamic shapeHelper)
{
return ContentShape("Parts_SchoenNavButton_Edit", () =>
shapeHelper.EditorTemplate(
TemplateName: "Parts/SchoenNavButton",
Model: part,
Prefix: Prefix));
}
protected override DriverResult Editor(SchoenNavButtonPart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
EditorTemplage
#model FishySoftware.SchoenBuilders.Models.SchoenNavButtonPart
<fieldset>
<legend>Button Details</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Url, T("Url"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Url)
#Html.ValidationMessageFor(model => model.Url)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Text, T("Text"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Text)
#Html.ValidationMessageFor(model => model.Text)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonWidth, T("Button Width"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonWidth)
#Html.ValidationMessageFor(model => model.ButtonWidth)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonHeight, T("Button Height"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonHeight)
#Html.ValidationMessageFor(model => model.ButtonHeight)
</div>
</fieldset>
Orchard is always using content of Record classes to save data to the database. You, on the other hand, are saving data from the browser inside the Part class and you haven't properly connected your Part and Record classes.
To do that, you'll have to change your ShoenNavButtonPart to this:
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url {
get { return Record.Url; }
set { Record.Url = value; }
}
public string Text {
get { return Record.Text; }
set { Record.Text = value; }
}
[DefaultValue(296)]
public int ButtonWidth {
get { return Record.ButtonWidth; }
set { Record.ButtonWidth = value; }
}
[DefaultValue(188)]
public int ButtonHeight {
get { return Record.ButtonHeight; }
set { Record.ButtonHeight = value; }
}
}

ValidationResult and ViewModels

I have recently created a new application that is using MVC4 and EF5. The tables already existed, so we are using Database first. This is all new to me, it is my first time in MVC or EF.
I have some models in my EMDX file, and in one of them I inherited IValidatableObject and put some code in the Validate function. This was working fine, then I changed my view to use a ViewModel, now I get an error from validate, and I am stumped. It is still calling my validate function, but no longer posting it back to the screen, i just get a yellow screen.
Error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Model:
public partial class Names : IValidatableObject {
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}
ViewModel:
public class NamesVM {
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
}
Controller: Edit Function:
[HttpPost]
public ActionResult Edit(NamesVM nvm) {
if (ModelState.IsValid) {
dbCommon.Entry(nvm.Name).State = EntityState.Modified;
dbCommon.SaveChanges();
return RedirectToAction("Index");
}
return View(nvm);
}
View:
#model NamesVM
<div class="editor-label">
#Html.LabelFor(model => model.Name.Id)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Id)
#Html.ValidationMessageFor(model => model.Name.Id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Name)
#Html.ValidationMessageFor(model => model.Name.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Active)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Active)
#Html.ValidationMessageFor(model => model.Name.Active)
</div>
<input type="submit" value="Save" />
The codes works fine if everything is correct on the screen, but when validation fails, i don't get a nice error, I get a yellow screen. I am sure I am missing something, I just don't know what.
finally solved it. The IValidatableObject needs to be moved to the ViewModel, as well as the logic for it.
Model
public partial class Names { //: IValidatableObject { //Remove the IValidateableObject from here
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
// this whole method can be commented out
//public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
//}
}
View Model
public class NamesVM : IValidatableObject { // add the IValidatableObject to your view model
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
// and move your validation logic to here
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}

Resources