SoFunction
Updated on 2025-03-07

MVC uses jQuery Template to achieve batch updates

Ideas

  • Query the js file required for jQuery Template:
  • Generate template content in <script type="text/x-jquery-tmpl" >/script> containing placeholders
  • When clicking the Add button, add the template content to the interface and assign a value to the placeholder.

The content of jQuery Template is roughly like this:

<script type="text/x-jquery-tmpl" >
<li style="padding-bottom:15px">
 
    <input autocomplete="off" name="" type="hidden" value="${index}" />
 
    <img src="/Content/images/" style="cursor: move" alt=""/>
 
    <label>Title</label>
    <input name="FavouriteMovies[${index}].Title" type="text" value="" />
 
    <label>Rating</label>
    <input name="FavouriteMovies[${index}].Rating" type="text" value="0" />
 
    <a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  onclick="$(this).parent().remove();">Delete</a>
</li>
</script>

In order to obtain the above content, we obtain it from the helper method:

    <script type="text/x-jquery-tmpl" >
            @("MovieEntryEditor", new Movie())
    </script>

Help class CollectionEditingHtmlExtensions:

The template content is also generated through this partial view, except that the generated content contains placeholders.

using System;
using ;
using ;
using ;
using ;
 
namespace 
{
    public static class CollectionEditingHtmlExtensions
    {
        /// &lt;summary&gt;
        /// The goal is to generate the following format        ///&lt;input autocomplete="off" name="" type="hidden" value="6d85a95b-1dee-4175-bfae-73fad6a3763b" /&gt;
        ///&lt;label&gt;Title&lt;/label&gt;
        ///&lt;input class="text-box single-line" name="FavouriteMovies[6d85a95b-1dee-4175-bfae-73fad6a3763b].Title" type="text" value="Movie 1" /&gt;
        ///&lt;span class="field-validation-valid"&gt;&lt;/span&gt;
        /// &lt;/summary&gt;
        /// &lt;typeparam name="TModel"&gt;&lt;/typeparam&gt;
        /// &lt;param name="html"&gt;&lt;/param&gt;
        /// <param name="collectionName">name of collection attribute</param>        /// &lt;returns&gt;&lt;/returns&gt;
        public static IDisposable BeginCollectionItem&lt;TModel&gt;(this HtmlHelper&lt;TModel&gt; html, string collectionName)
        {
            if ((collectionName))
            {
                throw new ArgumentException("collectionName is null or empty","collectionName");
            }
            string collectionIndexFieldName = ("{0}.Index", collectionName);//
            string itemIndex = null;
            if ((JQueryTemplatingEnabledKey))
            {
                itemIndex = "${index}";
            }
            else
            {
                itemIndex = GetCollectionItemIndex(collectionIndexFieldName);
            }
 
            //For example, FavouriteMovies[6d85a95b-1dee-4175-bfae-73fad6a3763b]            string collectionItemName = ("{0}[{1}]", collectionName, itemIndex);
 
            TagBuilder indexField = new TagBuilder("input");
            (new Dictionary&lt;string, string&gt;() {
                { "name", ("{0}.Index", collectionName) }, //name=""
                { "value", itemIndex },//value="6d85a95b-1dee-4175-bfae-73fad6a3763b"
                { "type", "hidden" },
                { "autocomplete", "off" }
            });
            (());
 
            return new CollectionItemNamePrefixScope(, collectionItemName);
        }
 
         private class CollectionItemNamePrefixScope : IDisposable
         {
             private readonly TemplateInfo _templateInfo;
             private readonly string _previousPrefix;
 
             public CollectionItemNamePrefixScope(TemplateInfo templateInfo, string collectionItemName)
             {
                 this._templateInfo = templateInfo;
                 _previousPrefix = ;
                  = collectionItemName;
             }
 
             public void Dispose()
             {
                 _templateInfo.HtmlFieldPrefix = _previousPrefix;
             }
         }
 
        /// &lt;summary&gt;
        /// Think of the key, store the Guid string in the context        /// If you add to a partial view, you will directly generate a Guid string        /// If it is an update, in order to maintain consistency with ModelState, iterate over the original Guid        /// &lt;/summary&gt;
        /// &lt;param name="collectionIndexFieldName"&gt;&lt;/param&gt;
        /// <returns>Return Guid string</returns>        private static string GetCollectionItemIndex(string collectionIndexFieldName)
        {
            Queue&lt;string&gt; previousIndices = (Queue&lt;string&gt;)[collectionIndexFieldName];
            if (previousIndices == null)
            {
                [collectionIndexFieldName] = previousIndices = new Queue&lt;string&gt;();
                string previousIndicesValues = [collectionIndexFieldName];
                if (!(previousIndicesValues))
                {
                    foreach (string index in (','))
                    {
                        (index);
                    }
                }
            }
            return  &gt; 0 ? () : ().ToString();
        }
 
        private const string JQueryTemplatingEnabledKey = "__BeginCollectionItem_jQuery";
        public static MvcHtmlString CollectionItemJQueryTemplate&lt;TModel, TCollectionItem&gt;(this HtmlHelper&lt;TModel&gt; html,
                                                                                    string partialViewName,
                                                                                    TCollectionItem modelDefaultValues)
        {
            ViewDataDictionary&lt;TCollectionItem&gt; viewData = new ViewDataDictionary&lt;TCollectionItem&gt;(modelDefaultValues);
            (JQueryTemplatingEnabledKey, true);
            return (partialViewName, modelDefaultValues, viewData);
        }
    }
}

Partial view

@using 
@model 
 
&lt;li style="padding-bottom: 15px;"&gt;
    @using (("FavouriteMovies"))
    {
        &lt;img src="@("~/Content/images/")" style="cursor: move" alt=""/&gt;
 
        @(model =&gt; )
        @(model =&gt; )
        @(model =&gt; )
 
        @(model =&gt; )
        @(model =&gt; )
        @(model =&gt; )
 
        &lt;a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" onclick=" $(this).parent().remove(); ">Delete the line</a>    }
&lt;/li&gt;

HomeController

        public ActionResult EditJqueryTemplate()
        {
            return View(CurrentUser);
        }
 
        [HttpPost]
        public ActionResult EditJqueryTemplate(User user)
        {
            if (!)
            {
                return View(user);
            }
            CurrentUser = user;
            return RedirectToAction("Display");
        }

The complete code is as follows:

@using 
@using 
@model 
 
@{
     = "EditJqueryTemplate";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
&lt;h2&gt;EditJqueryTemplate&lt;/h2&gt;
 
@using (())
{
    @(true)
    &lt;fieldset&gt;
        &lt;legend&gt;Favorite movies&lt;/legend&gt;
        @(model =&gt; )
        &lt;div class="editor-label"&gt;
            @(model =&gt; )
        &lt;/div&gt;
        &lt;div class="editor-field"&gt;
            @(model =&gt; )
            @(model =&gt; )
        &lt;/div&gt;
    &lt;/fieldset&gt;
    
    &lt;fieldset&gt;
        &lt;legend&gt;Favorite movies&lt;/legend&gt;
        @if ( == null ||  == 0)
        {
            &lt;p&gt;No movie you like to watch~~&lt;/p&gt;
        }
        &lt;ul  style="list-style-type: none"&gt;
            @if ( != null)
            {
                foreach (Movie movie in )
                {
                    ("MovieEntryEditor", movie);
                }
            }
        &lt;/ul&gt;
               
        &lt;a  href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Add</a>    &lt;/fieldset&gt;
    &lt;p&gt;
        &lt;input type="submit" value="submit" /&gt;
    &lt;/p&gt;
}
 
@section scripts
{
    &lt;script src="~/Scripts/"&gt;&lt;/script&gt;
    
    &lt;script type="text/x-jquery-tmpl" &gt;
            @("MovieEntryEditor", new Movie())
    &lt;/script&gt;
 
    &lt;script type="text/javascript"&gt;
            $(function () {
                $("#movieEditor").sortable();
 
                $('#addAnother').click(function() {
                    ();
                });
            });
 
            var viewModel = {
                addNew: function () {
                    $("#movieEditor").append($("#movieTemplate").tmpl({ index: viewModel._generateGuid() }));
                },
 
                _generateGuid: function () {
                    // Source: /questions/105034/how-to-create-a-guid-uuid-in-javascript/105074#105074
                    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                        var r = () * 16 | 0, v = c == 'x' ? r : (r &amp; 0x3 | 0x8);
                        return (16);
                    });
                }
            };
    &lt;/script&gt;
}

This is all about this article about MVC using jQuery Template to achieve batch updates. I hope it will be helpful to everyone's learning and I hope everyone will support me more.