2

I need users to be able to input hours into a table, potentially with fractional numbers, as follows:

Timetable

I've declared the following classes to hold the model data:

public class WeeklyTimes
{
    public decimal Week1 { get; set; }
    public decimal Week2 { get; set; }
    public decimal Week3 { get; set; }
    public decimal Week4 { get; set; }
    public decimal Week5 { get; set; }
}

public class MonthlyReport
{
    public WeeklyTimes TimeSpentTutoring { get; set; }
    public WeeklyTimes TimeSpentOnPreparation { get; set; }
    public WeeklyTimes TimeSpentOnHomework { get; set; }
}

And binding to the following view:

<table class="timesheet">
<tr class="timesheet-row">
    <th class="center-align" />
    <th class="center-align">Week 1</th>
    <th class="center-align">Week 2</th>
    <th class="center-align">Week 3</th>
    <th class="center-align">Week 4</th>
    <th class="center-align">Week 5</th>
</tr>
<tr class="timesheet-row">
    <th class="left-align">Tutoring Time</th>
    <td>@Html.TextBoxFor(x => x.TimeSpentTutoring.Week1, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentTutoring.Week2, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentTutoring.Week3, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentTutoring.Week4, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentTutoring.Week5, new { @class = "input-time" })</td>
</tr>
<tr class="timesheet-row">
    <th class="left-align">Learner Homework</th>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnHomework.Week1, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnHomework.Week2, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnHomework.Week3, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnHomework.Week4, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnHomework.Week5, new { @class = "input-time" })</td>
</tr>
<tr class="timesheet-row">
    <th class="left-align">Tutor Prep & Commute</th>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnPreparation.Week1, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnPreparation.Week2, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnPreparation.Week3, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnPreparation.Week4, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.TimeSpentOnPreparation.Week5, new { @class = "input-time" })</td>
</tr>
</table>

Two parts to this question:

  1. I'm new to MVC, and feel unsure about the amount of duplication in the HTML. Is there a better way to structure this HTML?
  2. What would be the best way to ensure appropriate validation? Ideally the user would only be able to enter numbers, formatted as follows: "#.#". However, using the decimal data-type, I can't seem to make this happen. I'm also consider using a string data-type and regular expression validation, but that seems strange to me.
Charlie
  • 15,069
  • 3
  • 64
  • 70

3 Answers3

2

First: you can create a template for WeeklyTimes

place on the EditorTemplates/WeeklyTimes.cshtml

you may want to add the label to the WeeklyTimes class

public class WeeklyTimes
{
    public string Name { get; set; }
    public decimal Week1 { get; set; }
    public decimal Week2 { get; set; }
    public decimal Week3 { get; set; }
    public decimal Week4 { get; set; }
    public decimal Week5 { get; set; }
}

WeeklyTimes.cshtml :

@model WeeklyTimes
<tr class="timesheet-row">
    <th class="left-align">@Model.Name</th>
    <td>@Html.TextBoxFor(x => x.Week1, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.Week2, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.Week3, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.Week4, new { @class = "input-time" })</td>
    <td>@Html.TextBoxFor(x => x.Week5, new { @class = "input-time" })</td>
</tr>

you may want another for MonthlyReport or just do it in the View as you wish

MonthlyReport :

@model MonthlyReport
<table class="timesheet">
    <tr class="timesheet-row">
        <th class="center-align" />
        <th class="center-align">Week 1</th>
        <th class="center-align">Week 2</th>
        <th class="center-align">Week 3</th>
        <th class="center-align">Week 4</th>
        <th class="center-align">Week 5</th>
    </tr>
    @EditorFor(m=> m.TimeSpentTutoring)
    @EditorFor(m=> m.TimeSpentOnPreparation)
    @EditorFor(m=> m.TimeSpentOnHomework)
</table>

Second: You can decorate your decimal like this for proper validation

[DisplayFormat(DataFormatString = "{0:#.#}", ApplyFormatInEditMode = true)]

for client side validation you can do:

$('input').on('change', function() {
    // this will add invalid class to the invalid inputs and you can make your css mark them with a red border or something. If you use 'input change' it will validate on every keystroke
    $('#myForm').validate();
});

and you can use a placeholder to improve usability :

@Html.TextBoxFor(x => x.Week1, new { @class = "input-time", placeholder="0.2" });

if you want to go for a better client side approach for browsers that support html5 input types you can do

@Html.TextBoxFor(x => x.Week1, new { @class = "input-time", placeholder="0.2", type="number" });

If this does not solve all your request, you must implement a custom validator. Check out this blog post for full details on how to accomplish that. http://www.codeproject.com/Articles/275056/Custom-Client-Side-Validation-in-ASP-NET-MVC3

Bart Calixto
  • 19,210
  • 11
  • 78
  • 114
  • That's great, for part 1 that definitely helps me decompose things. But on part 2, the text-boxes generated still accept any input. Ideally, a user wouldn't even be able to enter characters in these textboxes. – Charlie Aug 23 '13 at 04:13
  • @Charlie edited with client-side addon, trying to make you write less code but probably you should go down to create a custom validator. – Bart Calixto Aug 23 '13 at 04:27
  • You seem to be missing `@Html.ValidationMessageFor(...)`, maybe this is why you don't get any validation errors? – neeKo Aug 23 '13 at 05:18
  • I have a validation summary. The textboxes also turn red due to some CSS, so I know the validation is working correctly. What I am asking for is a numeric-only input. I don't even want the user to see alpha characters. – Charlie Aug 23 '13 at 05:44
  • Figured out part 2. I didn't have client-side validation scripts referenced, and I needed to use some better JavaScript functions such as those found here: http://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input?page=1&tab=votes#tab-top Still, I appreciate the effort you put into this question, as you led me to the answers. You win the bounty. EDIT: Turns out I have to wait a full day before awarding the bounty. – Charlie Aug 23 '13 at 06:17
  • @Charlie as I mentioned, type="number" restrict alpha characters. – Bart Calixto Aug 23 '13 at 16:07
  • @Charlie I was wondering that if you really need that input style validation, you can include a polyfill for those browsers that do not support input type number. There's one here: https://github.com/jonstipe/number-polyfill haven't tried it myself but I'm confident it will work for your purpose. – Bart Calixto Aug 26 '13 at 22:06
1

@Bart ans solved your first problem.

Your second problem try this

<Script>
    function Numberonly() {
                            alert('hi');
                        var reg = /^((\d{0,9})*(\.\d{0,2})|(\d{0,9})*)$/;

                        if (!reg.test(($("#txtWeek1").val()))) {
                            $("#txtWeek1").val('');
                            return false;
                        }
                    }
</Script>

View

@Html.TextBoxFor(x => x.Week1, new { @class = "input-time" ,@id="txtWeek1", @onkeyup = "Numberonly()" })

It allow only Decimal Number Ex:123.12 Point after two degit.

Jaimin
  • 7,964
  • 2
  • 25
  • 32
  • You get an upvote for making it clear that this requires JavaScript, but searching similar questions found much more robust answers. See here:http://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input?page=1&tab=votes#tab-top – Charlie Aug 23 '13 at 06:12
  • @ Charlie Yes you can use `Type="number"`. Thanks for your post. – Jaimin Aug 23 '13 at 06:14
-1

1) You can indeed limit this Html. You have two options here. The first is to call an Html helper for the entire model with @Html.EditorForModel(). This may not be what you want though, as you have three sub entities within your overall model. I would suggest something more along the lines of looping through all properties for each entity.

Ex:

@foreach (var property in Model.TimeSpentTutoring.GetType().GetProperties()) {
    ...
}

Then you just need to create a textbox on each pass. Here is a link that deals with a similar situation.


2) The DisplayFormat data attribute can be used to format numbers.

Ex (#.##):

[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
public decimal Week1 { get; set; }
...
Community
  • 1
  • 1
ElliotSchmelliot
  • 7,322
  • 4
  • 41
  • 64