1

I want to add input to form dynamically with jQuery templates.

My viewModel for rendering form is listed below

public class FormViewModel
{
    public int Id { get; set; }

    [Required]
    [StringLength(25, ErrorMessage = "Max firstname length is 25 symbols.")]
    [DisplayName("First name")]
    public string FirstName { get; set; }

    [Required]
    [StringLength(25, ErrorMessage = "Max lastname length is 25 symbols.")]
    [DisplayName("Last name")]
    public string LastName { get; set; }

    [Required]
    [Email(ErrorMessage = "Provide correct email address, please.")]
    [DisplayName("Email")]
    public string Email { get; set; }

    [Range(16, 150, ErrorMessage = "Age should be between 16 and 150.")]
    [DisplayName("Age")]
    public int? Age { get; set; }

    public IList<DiscountCode> Discounts { get; set; }
}

This is my model which I use for inputs that will be created dynamically.

public class DiscountCode
    {
        [Required]
        [DisplayName("Code name")]
        [StringLength(10, ErrorMessage = "Max name length is 10 symbols.")]
        public string Code { get; set; }

        [Required]
        [DisplayName("Code discount")]
        [Integer(ErrorMessage = "The field Percent should be a positive non-decimal number")]
        [Range(1,60, ErrorMessage = "The field Percent should be between 1 and 60.")]
        public int Percent { get; set; }
    }

I have this partial view for rendering DiscountCode inputs

@using DynamicForm.Models
@model FormViewModel
@if (Model != null && Model.Discounts != null)
{
    for (int i = 0; i < Model.Discounts.Count; i++)
    {
        <div class="row">
            <input type="hidden" name="Discounts.Index" value="@i" />
            <div class="col-md-4 form-group">
                <div class="input-group">
                    @Html.TextBoxFor(m => m.Discounts[i].Code, new { @class = "form-control " })
                    @Html.ValidationMessageFor(m => m.Discounts[i].Code, string.Empty, new { @class = "help-block" })
                </div>
            </div>
            <div class="col-md-6 form-group">
                <div class="input-group">
                    @Html.LabelFor(m => m.Discounts[i].Percent, new { @class = "control-label" })
                    @Html.TextBoxFor(m => m.Discounts[i].Percent, new { @class = "form-control " })
                    @Html.ValidationMessageFor(m => m.Discounts[i].Percent, string.Empty, new { @class = "help-block" })
                </div>
            </div>
            <div class="col-md-2 form-group">
                <div class="input-group">
                    <button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
                </div>
            </div>
        </div>
    }
}

And for adding discount inputs I use this code snippet

var data = { index: lastIndex };
var html = $.templates("#discountRow").render(data);
$(html).appendTo($discountsContainer);

and this template

<script id="discountRow" type="text/x-jsrender">
    <div class="row">
        <input type="hidden" name="Discounts.Index" value="{{: index}}">
        <div class="col-md-4 form-group">
            <div class="input-group">
                <label class="control-label" for="Discounts_{{: index}}__Code">Code name</label>
                <input class="form-control " data-val="true" data-val-required="Code is required" data-val-length="Max name length is 10 symbols." data-val-length-max="10"
                       id="Discounts_{{: index}}__Code" name="Discounts[{{: index}}].Code" type="text" value="">
                <span class="field-validation-valid help-block" data-valmsg-for="Discounts[{{: index}}].Code" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="col-md-6 form-group">
            <div class="input-group">
                <label class="control-label" for="Discounts_{{: index}}__Percent">Code discount</label>
                <input class="form-control " data-val="true" data-val-required="Percent is required" data-val-number="The field Code discount must be a number."
                       data-val-range="The field Percent should be between 1 and 60." data-val-range-max="60" data-val-range-min="1"
                       data-val-regex="The field Percent should be a positive non-decimal number."
                       data-val-regex-pattern="^-?\d+$" data-val-required="The Code discount field is required."
                       id="Discounts_{{: index}}__Percent" name="Discounts[{{: index}}].Percent" type="text" value="0" aria-required="true" aria-invalid="false"
                       aria-describedby="Discounts_{{: index}}__Percent-error">
                <span class="help-block field-validation-valid" data-valmsg-for="Discounts[{{: index}}].Percent" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="col-md-2 form-group">
            <div class="input-group">
                <button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
            </div>
        </div>
    </div>
</script>

As you can see I just copy output of razor and insert it in template. So if I change validation in model I'll change template each time. How to generate this template automatic with preserving all data attributes for client-side validation ?

1
  • why can't you create variable for validation and pass to view as you would any other data? Commented Jul 27, 2014 at 11:58

1 Answer 1

2

You can generate templte code like you create your input code, but Model.Discounts must have at least one element. See code below. I add DiscountCode to discounts if its empty and change some html attributes to make template display as you want;)

if (Model.Discounts == null || Model.Discounts.Count <= 0)
{
    Model.Discounts = new List<DiscountCode> { new DiscountCode() };
}
<script id="discountRow" type="text/x-jsrender">
    <div class="row">
        <input type="hidden" name="Discounts.Index" value="{{: index}}" />
        <div class="col-md-4 form-group">
            <div class="input-group">
                @Html.LabelFor(m => m.Discounts[0].Percent, new { @class = "control-label", For = "Discounts[{{: index}}].Code" })
                @Html.TextBoxFor(m => m.Discounts[0].Code, new { @class = "form-control ", Id = "Discounts_{{: index}}__Code", Name = "Discounts[{{: index}}].Code", Value="" } )
                @Html.ValidationMessageFor(m => m.Discounts[0].Code, string.Empty, new { @class = "help-block", Data_Valmsg_For = "Discounts[{{: index}}].Code" })
            </div>
        </div>
        <div class="col-md-6 form-group">
            <div class="input-group">
                @Html.LabelFor(m => m.Discounts[0].Percent, new { @class = "control-label", For = "Discounts[{{: index}}].Percent" })
                @Html.TextBoxFor(m => m.Discounts[0].Percent, new { @class = "form-control ", Id = "Discounts_{{: index}}__Percent", Name = "Discounts[{{: index}}].Percent", Value = "" })
                @Html.ValidationMessageFor(m => m.Discounts[0].Percent, string.Empty, new { @class = "help-block", Data_Valmsg_For = "Discounts[{{: index}}].Percent" })
            </div>
        </div>
        <div class="col-md-2 form-group">
            <div class="input-group">
                <button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
            </div>
        </div>
    </div>
</script>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.