I have a model defined this way:
public class AdvisoryViewModel : IValidatableObject
{
[Display(Name = "Start Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true)]
public DateTime? StartDate { get; set; }
[Display(Name = "End Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true)]
public DateTime? EndDate { get; set; }
[Display(Name = "Instructions")]
[Required(ErrorMessage = "Instructions are required")]
[MaxLength(500, ErrorMessage = "Instructions cannot be longer than 500 characters.")]
public string Instruction { get; set; }
IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
{
List<ValidationResult> results = new List<ValidationResult>();
if (StartDate.HasValue &&
EndDate.HasValue &&
StartDate.Value > EndDate.Value)
{
ValidationResult result = new ValidationResult("Start date must be after end date.");
results.Add(result);
}
return results;
}
And I am validating it as follows:
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(advisoryViewModel, new ValidationContext(advisoryViewModel), validationResults, true))
{
return Json(new { success = false, message = string.Join("; ", validationResults.Select(r => r.ErrorMessage)) });
}
What happens on validation is it first only calls the "Required" attributes - for example, if the start date is later than end date AND the instructions are null, it returns with only the message that instructions cannot be null. Once they are not null, it returns the start/end date error message.
Is there a way to have it do ALL of the validations up front rather than two attempts?
Also, is there a way the start/end validation can be added to client side results?
Related
I have two text inputs that appear to be set up exactly the same, however while one gets the decimal places automatically added to it when you click out of the text box, the other one does not. I'm at a loss as to why this could be happening, any help would be much appreciated.
Works:
[Display(Name = #"Extra Withholding")]
[UIHint("GridFormFieldDecimal")]
[DisplayFormat(DataFormatString = "{0:0.00}", ApplyFormatInEditMode = true)]
public decimal? Federal2020ExtraWithholding { get; set; }
#Html.EditorFor(m => m.Federal2020ExtraWithholding,
new { SecondaryLabel = "W-4 Step 4 (c)",
OuterCss = "grid_8 alpha",
LabelCss = "grid_4",
InputCss = "grid_4",
HelpCss = "grid_4 display-none",
TextCss = "dollars -disable-on-exempt",
Prefix = "$"
})
Doesn't work:
[Display(Name = #"Other Income")]
[UIHint("GridFormFieldDecimal")]
[DisplayFormat(DataFormatString = "{0:0.00}", ApplyFormatInEditMode = true)]
public decimal? OtherIncomeAmount { get; set; }
#Html.EditorFor(m => m.OtherIncomeAmount,
new { SecondaryLabel = "W-4 Step 4 (a)",
OuterCss = "grid_8 alpha",
LabelCss = "grid_4",
InputCss = "grid_4",
HelpCss = "grid_4 display-none",
TextCss = "dollars -disable-on-exempt",
Prefix = "$"
})
I am having some trouble populating the tables when running the code online. Appointment date that I created in the DbInitializer appears but the StartTime and EndTime aren't showing online.
I think it has something to do with my appointments controller. If anyone can help that would be much appreciated.
This is the index part of the AppointmentController
public async Task<IActionResult> Index(string sortOrder)
{
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
var appointments = from s in _context.Appointments.Include(a => a.Customer).Include(a => a.Staff)
select s;
switch (sortOrder)
{
case "Date":
appointments = appointments.OrderBy(s => s.AppointmentDate);
break;
case "date_desc":
appointments = appointments.OrderByDescending(s => s.AppointmentDate);
break;
default:
appointments = appointments.OrderBy(s => s.AppointmentDate);
break;
}
return View(await appointments.AsNoTracking().ToListAsync());
}
This is the model for the Appointments
{
public class Appointment
{
public int AppointmentID { get; set; }
public int CustomerID { get; set; }
public int StaffID { get; set; }
//[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
//[Display(Name = "Appointment Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Appointment Date")]
public DateTime AppointmentDate { get; set; }
[DataType(DataType.Time)]
[DisplayFormat(DataFormatString = "{00:00:00}", ApplyFormatInEditMode = true)]
[Display(Name = "End Time")]
public DateTime EndTime { get; set; }
[DataType(DataType.Time)]
[DisplayFormat(DataFormatString = "{00:00:00}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Time")]
public DateTime StartTime { get; set; }
public Customer Customer { get; set; }
public Staff Staff { get; set; }
}
}
select s;
switch (sortOrder)
{
case "Date":
appointments = appointments.OrderBy(s => s.AppointmentDate);
break;
case "date_desc":
appointments = appointments.OrderByDescending(s => s.AppointmentDate);
break;
default:
appointments = appointments.OrderBy(s => s.AppointmentDate);
break;
}
return View(await appointments.AsNoTracking().ToListAsync());
}
UPDATE
This is the code that is in my DbInitializer
var appointments = new Appointment[]
{
new Appointment{CustomerID=1,StaffID=1,AppointmentDate=DateTime.Parse("20/07/2017"),StartTime=DateTime.Parse("16:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=2,StaffID=2,AppointmentDate=DateTime.Parse("20/05/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=3,StaffID=3,AppointmentDate=DateTime.Parse("20/04/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=4,StaffID=4,AppointmentDate=DateTime.Parse("20/03/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=5,StaffID=5,AppointmentDate=DateTime.Parse("20/02/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=6,StaffID=6,AppointmentDate=DateTime.Parse("20/09/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=7,StaffID=7,AppointmentDate=DateTime.Parse("20/08/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")},
new Appointment{CustomerID=8,StaffID=8,AppointmentDate=DateTime.Parse("20/06/2017"),StartTime=DateTime.Parse("17:00:00"),EndTime=DateTime.Parse("17:00:00")}
};
foreach (Appointment c in appointments)
{
context.Appointments.Add(c);
}
context.SaveChanges();
}
This is a screen shot of the appointments part. I have populated the database to show a start and end time but as you can see nothing is being pulled through.
My date is displayed like dd/mm/yyyy. I want it displayed like yyyy-mm-dd.
#Html.EditorFor(x=> x.DateChosen, new { htmlAttributes = new { #id = "choosetime" } })
I already tried
#Html.EditorFor(x=> x.DateChosen, new { htmlAttributes = new { #id = "choosetime", "{0:dd/MM/yyyy}" } })
What you are looking for is DisplayFormatAttribute :
public class MyModel
{
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateChosen { get; set; }
// Other properties goes there
}
And use your Html helper like this :
#Html.EditorFor(x=> x.DateChosen, new { htmlAttributes = new { #id = "choosetime" } })
Edit 1 :
Base on comment, if you need to access the string representation of the DateChosen property somewhere else in your code, you can update your model like that :
public class MyModel
{
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateChosen { get; set; }
public string DateChosenString
{
return DateChosen.ToString("yyyy-MM-dd");
}
// Other properties goes there
}
I have a View that contains an Model, this model contains a list(of another Model) and this list is filled in my view with Dynamic repeating fields;
Model:
public class CAOEntry
{
public CAOEntry()
{
this.TimeEntries = new List<TimeEntry>() { new TimeEntry() };
}
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
public List<TimeEntry> TimeEntries {get; set;}
}
My View(the part that creates the handles the TimeEntry List)
#Html.EditorFor(model => model.TimeEntries)
<button data-bind="click: addTimeEntry">Voeg tijdsvak toe</button>
<button data-bind="click: removeTimeEntry">Verwijder laatste</button>:
the data-bind"click:addTimeEntry" is a jQuery/KnockoutJs script that adds new time entries. These are added from an Editor Template.
In this Editor template i got the following line of code:
#Html.EnumDropDownListFor(model => model.DayOfWeekOrHoliday,"--Selecteer een dag--", new { htmlAttributes = new { #class = "form-control", data_bind = "value: DayOfWeekOrHoliday, namePath: true" } })
Now the following happens when i have, for example two, TimeEntries filled in and click on save;
There are three time entries returned to my controller:
One containing the first filled Enum Value, other fields are null.
the other two don't contain any Enum values but have the other fields filled.
When debugging the view, i saw the following problem;
TimeEntry 1:
enum field;
id="TimeEntries_0__DayOfWeekOrHoliday" name="TimeEntries[0].DayOfWeekOrHoliday"
Example of a second field:
id="TimeEntries_1__TimeFrom" name="TimeEntries[1].TimeFrom"
TimeEntry 2:
Enum field;
id="TimeEntries_0__DayOfWeekOrHoliday" name="TimeEntries[0].DayOfWeekOrHoliday"
Example of a second field:
id="TimeEntries_2__TimeFrom" name="TimeEntries[2].TimeFrom"
so the Array is not incremented right for the enum value. Is this because there is an enum field in an EditorFor helper?
I can't figure out how to solve this.
[Edit]
TimeEntry Class:
public class TimeEntry
{
public int ID { get; set; }
//1:Monday,2:Tuesday,3:Wednesday,4:Thursday,5:Friday,6:Saturday,7:Sunday,8:Any Holiday
public enum Days {Maandag =1,Dinsdag =2, Woensdag=3,Donderdag = 4, Vrijdag= 5, Zaterdag=6, Zondag=7, Feestdag = 8}
[Required]
[Display(Name = "Dag")]
[Range(1, int.MaxValue, ErrorMessage = "Selecteer een dag")]
public Days DayOfWeekOrHoliday { get; set; }
[Required]
[Display(Name = "Start Tijdvak(uur:minuten)")]
[StringLength(5, MinimumLength = 5)]
[DataType(DataType.Time)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm}")]
public string TimeFrom { get; set; }
[Required]
[Display(Name = "Eind Tijdvak(uur:minuten)")]
[StringLength(5, MinimumLength = 5)]
[DataType(DataType.Time)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm}")]
public string TimeTo { get; set; }
[Required]
public decimal Premium { get; set; }
}
EditorFor:
function createViewModel() {
var createTimeEntry = function () {
return {
DayOfWeekOrHoliday: ko.observable(),
TimeFrom: ko.observable(),
TimeTo: ko.observable(),
Premium: ko.observable()
};
};
var addTimeEntry = function () {
TimeEntries.push(createTimeEntry());
};
var removeTimeEntry = function () {
TimeEntries.pop();
};
var ValidFrom = ko.observable();
var ValidTo = ko.observable();
var TimeEntries = ko.observableArray([createTimeEntry()]);
return {
ValidFrom: ValidFrom,
ValidTo: ValidTo,
TimeEntries: TimeEntries,
addTimeEntry: addTimeEntry,
removeTimeEntry: removeTimeEntry
};
}
$(document).ready(function () {
var viewModel = createViewModel();
ko.applyBindings(viewModel);
});
</script>
Validator.TryValidateObject everytime return true. Why? I tried different field values.
Its my model:
public class CompanyPreviewMeta
{
[Required]
[Display(Name="Изображение")]
public string Image { get; set; }
[Required]
[Display(Name="Текст")]
[StringLength(100, MinimumLength = 20, ErrorMessage = "Значение {0} должно содержать от {2} до 100 символов.")]
public string Text { get; set; }
}
It's my validation, and "valid" is true everytime:
ValidationContext ValidatorContext = new ValidationContext(model, null, null);
List<ValidationResult> result = new List<ValidationResult>();
bool valid = Validator.TryValidateObject(model, ValidatorContext, result, true);