Post a form in ASP.NET Core

This article is about forms in ASP.NET Core, how to validate them and how to post the data. The purpose of a form is to take input data, validate this data and save the data in a database.

To create and post a form in ASP.NET Core you need a View, a Controller and a Model. You can submit the form directly to the controller or use ajax/javascript to submit the form to a controller. Form validation is usually done with a combination of javascript and controller methods.

Form encoding

Form data can be encoded in 3 different ways, the type used is specified in the enctype attribute in the form tag.

application/x-www-form-urlencoded: Data is sent as key-value-pairs and all data is encoded.

multipart/form-data: Data is sent as sections and no data is encoded. This type must be used if you have a file upload control in your form.

text/plain: Data is sent as it is, no encoding. Can be used to send a JSON-document or an XML-document.

Libraries

We need some jquery/javascript libraries to be able to handle our form. These libraries is imported in our standard layout view, it is convient to import all libraries in one place. Jquery is used to post the form with ajax and is used in validation. Toastr is used to show small messages to the user and Font Awesome is used to get nice icons. Jquery validation and Jquery validate unobtrusive is used to validate the form.

<environment names="Development">
    <link href="@this.tools.GetFilePath("/css/toastr.css")" rel="stylesheet" />
    <script src="/js/jquery/v3.3.1/jquery.js"></script>
    <script src="/js/jquery-validation/v1.17.0/jquery.validate.js"></script>
    <script src="/js/jquery-validation-unobtrusive/v3.2.8/jquery.validate.unobtrusive.js"></script>
    <script src="/js/toastr/v2.1.4/toastr.js"></script>
    <script src="/js/js-cookie/v2.2.0/js.cookie.js"></script>
    <script src="/js/font-awesome/v5.3.1/all.js"></script>
</environment>
<environment names="Staging,Production">
    <link href="@this.tools.GetFilePath("/css/toastr.min.css")" rel="stylesheet" />
    <script src="/js/jquery/v3.3.1/jquery.min.js"></script>
    <script src="/js/jquery-validation/v1.17.0/jquery.validate.min.js"></script>
    <script src="/js/jquery-validation-unobtrusive/v3.2.8/jquery.validate.unobtrusive.min.js"></script>
    <script src="/js/toastr/v2.1.4/toastr.min.js"></script>
    <script src="/js/js-cookie/v2.2.0/js.cookie.min.js"></script>
    <script src="/js/font-awesome/v5.3.1/all.min.js"></script>
</environment>

Models

The main models for our form is ResponseData, StaticPageDocument and StaticPageDetail. ResponseData is used to display messages to the user and the other two models is important to display and save data in the form.

public class ResponseData
{
    #region variables

    public bool success { get; set; }
    public string id { get; set; }
    public string message { get; set; }
    public string url { get; set; }

    #endregion

    #region Constructors

    public ResponseData()
    {
        // Set values for instance variables
        this.success = false;
        this.id = "";
        this.message = "";
        this.url = "";

    } // End of the constructor

    public ResponseData(bool success, string id, string message)
    {
        // Set values for instance variables
        this.success = success;
        this.id = id;
        this.message = message;
        this.url = "";

    } // End of the constructor

    public ResponseData(bool success, string id, string message, string url)
    {
        // Set values for instance variables
        this.success = success;
        this.id = id;
        this.message = message;
        this.url = url;

    } // End of the constructor

    #endregion

} // End of the class
public class StaticPageDocument
{
    #region Variables

    public string id { get; set; }
    public string model_type { get; set; }
    public Int32 connection_id { get; set; }
    public string meta_robots { get; set; }
    public Int32 show_as_page { get; set; }
    public string page_name { get; set; }
    public DateTime date_updated { get; set; }
    public string main_image { get; set; }
    public IList<string> images { get; set; }
    public IList<string> keywords { get; set; }
    public IDictionary<string, StaticPageDetail> translations { get; set; }

    #endregion

    #region Constructors

    public StaticPageDocument()
    {
        // Set values for instance variables
        this.id = Guid.NewGuid().ToString();
        this.model_type = "static_page";
        this.connection_id = 0;
        this.meta_robots = "";
        this.show_as_page = 0;
        this.page_name = "";
        this.date_updated = DateTime.UtcNow;
        this.main_image = "";
        this.images = new List<string>();
        this.keywords = new List<string>();
        this.translations = new Dictionary<string, StaticPageDetail>();

    } // End of the constructor

    #endregion

    #region Get methods

    public StaticPageDetail GetTranslation(string key)
    {
        // Create the string to return
        StaticPageDetail translation = new StaticPageDetail();

        // Check if the dictionary contains the key
        if (this.translations.ContainsKey(key))
        {
            translation = this.translations[key];
        }

        // Return the value
        return translation;

    } // End of the GetTranslation method

    #endregion

} // End of the class
public class StaticPageDetail
{
    #region Variables

    public string link_name { get; set; }
    public string title { get; set; }
    public string text_html { get; set; }
    public string meta_description { get; set; }
    public string meta_keywords { get; set; }
        
    #endregion

    #region Constructors

    public StaticPageDetail()
    {
        // Set values for instance variables
        this.link_name = "";
        this.title = "";
        this.text_html = "";
        this.meta_description = "";
        this.meta_keywords = "";

    } // End of the constructor

    #endregion

} // End of the class

Controller

This controller includes methods that we need to administer static pages, it is the link between views and repositories. This controller should return views and handle form postings.

public class admin_static_pagesController : BaseController
{
    #region Variables

    private readonly IStaticPageRepository static_page_repository;
    private readonly ILanguageRepository language_repository;
    private readonly IWebDomainRepository web_domain_repository;
    private readonly IStaticTextRepository static_text_repository;
    private readonly IAdministratorRepository administrator_repository;
    private readonly IBlobStorageRepository blob_storage_repository;

    #endregion

    #region Constructors

    public admin_static_pagesController(IRazorViewEngine view_engine, IStaticPageRepository static_page_repository, ILanguageRepository language_repository, 
        IWebDomainRepository web_domain_repository,  IStaticTextRepository static_text_repository, IAdministratorRepository administrator_repository,
        IBlobStorageRepository blob_storage_repository) : base(view_engine)
    {
        // Set values for instance variables
        this.static_page_repository = static_page_repository;
        this.language_repository = language_repository;
        this.web_domain_repository = web_domain_repository;
        this.static_text_repository = static_text_repository;
        this.administrator_repository = administrator_repository;
        this.blob_storage_repository = blob_storage_repository;

    } // End of the constructor

    #endregion

    #region View methods

    [HttpGet]
    [Authorize(Roles = "Administrator,Editor,Translator")]
    public async Task<IActionResult> index()
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get keywords
        string kw = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("kw") == true)
        {
            kw = ControllerContext.HttpContext.Request.Query["kw"];
        }

        // Get the sort field
        string sf = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("sf") == true)
        {
            sf = ControllerContext.HttpContext.Request.Query["sf"];
        }

        // Get the sort order
        string so = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("so") == true)
        {
            so = ControllerContext.HttpContext.Request.Query["so"];
        }

        // Get the page size
        Int32 pz = 10;
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("pz") == true)
        {
            Int32.TryParse(ControllerContext.HttpContext.Request.Query["pz"], out pz);
        }

        // Add data to the view
        ViewBag.CurrentDomain = current_domain;
        ViewBag.TranslatedTexts = tt;
        ViewBag.AuthorizationLevel = this.administrator_repository.GetAuthorizationLevel(ControllerContext.HttpContext.User);
        ViewBag.Keywords = kw;
        ViewBag.SortField = sf;
        ViewBag.SortOrder = so;
        ViewBag.PageSize = pz;

        // Return the view
        return View();

    } // End of the index method

    [HttpGet]
    [Authorize(Roles = "Administrator,Editor")]
    public async Task<IActionResult> edit(string id = "")
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get keywords
        string kw = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("kw") == true)
        {
            kw = ControllerContext.HttpContext.Request.Query["kw"];
        }

        // Get the sort field
        string sf = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("sf") == true)
        {
            sf = ControllerContext.HttpContext.Request.Query["sf"];
        }

        // Get the sort order
        string so = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("so") == true)
        {
            so = ControllerContext.HttpContext.Request.Query["so"];
        }

        // Get the page size
        Int32 pz = 10;
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("pz") == true)
        {
            Int32.TryParse(ControllerContext.HttpContext.Request.Query["pz"], out pz);
        }

        // Get items
        ModelItem<StaticPageDocument> static_page_model = await this.static_page_repository.GetById(id);

        // Add data to the view
        ViewBag.CurrentDomain = current_domain;
        ViewBag.TranslatedTexts = tt;
        ViewBag.AuthorizationLevel = this.administrator_repository.GetAuthorizationLevel(ControllerContext.HttpContext.User);
        ViewBag.Post = static_page_model.item != null ? static_page_model.item : new StaticPageDocument();
        ViewBag.Keywords = kw;
        ViewBag.SortField = sf;
        ViewBag.SortOrder = so;
        ViewBag.PageSize = pz;

        // Return the edit view
        return View("edit");

    } // End of the edit method

    [HttpGet]
    [Authorize(Roles = "Administrator,Editor,Translator")]
    public async Task<IActionResult> translate(string id = "", string lang = "")
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get keywords
        string kw = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("kw") == true)
        {
            kw = ControllerContext.HttpContext.Request.Query["kw"];
        }

        // Get the sort field
        string sf = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("sf") == true)
        {
            sf = ControllerContext.HttpContext.Request.Query["sf"];
        }

        // Get the sort order
        string so = "";
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("so") == true)
        {
            so = ControllerContext.HttpContext.Request.Query["so"];
        }

        // Get the page size
        Int32 pz = 10;
        if (ControllerContext.HttpContext.Request.Query.ContainsKey("pz") == true)
        {
            Int32.TryParse(ControllerContext.HttpContext.Request.Query["pz"], out pz);
        }

        // Get items
        ModelItem<LanguagesDocument> languages_model = await this.language_repository.GetByType();
        ModelItem<StaticPageDocument> static_page_model = await this.static_page_repository.GetById(id);

        // Add data to the view
        ViewBag.CurrentDomain = current_domain;
        ViewBag.TranslatedTexts = tt;
        ViewBag.AuthorizationLevel = this.administrator_repository.GetAuthorizationLevel(ControllerContext.HttpContext.User);
        ViewBag.Post = static_page_model.item;
        ViewBag.LanguageCode = lang;
        ViewBag.Languages = languages_model.item;
        ViewBag.Keywords = kw;
        ViewBag.SortField = sf;
        ViewBag.SortOrder = so;
        ViewBag.PageSize = pz;

        // Redirect the user if the post is null
        if (ViewBag.Post == null)
        {
            // Redirect the user to the list
            return Redirect("/admin_static_pages");
        }

        // Return the view
        return View();

    } // End of the translate method

    #endregion

    #region Post methods

    [HttpPost]
    public IActionResult search(IFormCollection collection)
    {
        // Get the search data
        string keywordString = collection["txtSearch"];
        string sort_field = collection["selectSortField"];
        string sort_order = collection["selectSortOrder"];
        string page_size = collection["selectPageSize"];

        // Return the url with search parameters
        return Redirect("/admin_static_pages?kw=" + WebUtility.UrlEncode(keywordString) + "&sf=" + sort_field + "&so=" + sort_order + "&pz=" + page_size);

    } // End of the search method

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Administrator,Editor")]
    public async Task<IActionResult> edit(IFormCollection collection)
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get the id
        string id = collection["txtId"].ToString();

        // Get the static page document
        ModelItem<StaticPageDocument> static_page_model = await this.static_page_repository.GetById(id);
        StaticPageDocument post = static_page_model.item;

        // Make sure that the post not is null
        if (post == null)
        {
            post = new StaticPageDocument();
            post.id = id;
        }

        // Update values
        post.connection_id = DataValidation.ParseInt32(collection["selectConnection"].ToString(), 0);
        post.meta_robots = collection["selectMetaRobots"].ToString();
        post.show_as_page = DataValidation.ParseInt32(collection["cbShowAsPage"], 0);
        post.date_updated = DateTime.UtcNow;
        post.page_name = collection["txtPageName"].ToString();
        post.main_image = collection["txtMainImage"].ToString();
        post.keywords = collection["txtKeywords"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries);

        // Make sure that keywords is lowercase and trimmed
        for (int i = 0; i < post.keywords.Count; i++)
        {
            post.keywords[i] = post.keywords[i].ToLower().Trim();
        }

        if (post.translations.ContainsKey(current_domain.back_end_language_code) == true)
        {
            // Get the detail post
            StaticPageDetail dp = post.translations[current_domain.back_end_language_code];
            dp.link_name = collection["txtLinkName"].ToString();
            dp.meta_description = collection["txtMetaDescription"].ToString();
            dp.meta_keywords = collection["txtMetaKeywords"].ToString();
            dp.text_html = collection["txtTextHtml"].ToString();
            dp.title = collection["txtTitle"].ToString();
        }
        else
        {
            // Create a new detail post
            StaticPageDetail dp = new StaticPageDetail();
            dp.link_name = collection["txtLinkName"].ToString();
            dp.meta_description = collection["txtMetaDescription"].ToString();
            dp.meta_keywords = collection["txtMetaKeywords"].ToString();
            dp.text_html = collection["txtTextHtml"].ToString();
            dp.title = collection["txtTitle"].ToString();

            // Add the detail post
            post.translations.Add(current_domain.back_end_language_code, dp);
        }

        // Verify the page name
        ResponseData data = await verify_page_name(tt, post.id, post.page_name);

        // Return an error response
        if (data.success == false)
        {
            return Json(data: data);
        }

        // Add or update the post
        await this.static_page_repository.Upsert(post);

        // Return a success response
        return Json(data: new ResponseData(true, "", String.Format(tt.Get("success_post_updated"), tt.Get("static_page") + " (" + post.id + ")")));

    } // End of the edit method

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Administrator,Editor,Translator")]
    public async Task<IActionResult> translate(IFormCollection collection)
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get the id
        string id = collection["txtId"].ToString();
        string language_code = collection["selectLanguage"].ToString();

        // Get the static page document
        ModelItem<StaticPageDocument> static_page_model = await this.static_page_repository.GetById(id);
        StaticPageDocument post = static_page_model.item;

        // Make sure that the post not is null
        if (post == null)
        {
            // Return an error response
            return Json(data: new ResponseData(false, "", String.Format(tt.Get("error-field-invalid"), tt.Get("id") + ": " + post.id)));
        }

        // Update values
        if (post.translations.ContainsKey(language_code) == true)
        {
            // Update the detail post
            StaticPageDetail dp = post.translations[language_code];
            dp.link_name = collection["txtTranslatedLinkName"].ToString();
            dp.meta_description = collection["txtTranslatedMetaDescription"].ToString();
            dp.meta_keywords = collection["txtTranslatedMetaKeywords"].ToString();
            dp.text_html = collection["txtTranslatedTextHtml"].ToString();
            dp.title = collection["txtTranslatedTitle"].ToString();
        }
        else
        {
            // Create a new detail post
            StaticPageDetail dp = new StaticPageDetail();
            dp.link_name = collection["txtTranslatedLinkName"].ToString();
            dp.meta_description = collection["txtTranslatedMetaDescription"].ToString();
            dp.meta_keywords = collection["txtTranslatedMetaKeywords"].ToString();
            dp.text_html = collection["txtTranslatedTextHtml"].ToString();
            dp.title = collection["txtTranslatedTitle"].ToString();

            // Add the detail post
            post.translations.Add(language_code, dp);
        }

        // Update the post
        await this.static_page_repository.Update(post);

        // Return a success response
        return Json(data: new ResponseData(true, "", String.Format(tt.Get("success_post_updated"), tt.Get("static_page") + " (" + post.id + ")")));

    } // End of the translate method

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Administrator,Editor,Translator")]
    public async Task<IActionResult> images(IFormCollection collection)
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get the id
        string id = collection["txtId"].ToString();

        // Get the static page document
        ModelItem<StaticPageDocument> static_page_model = await this.static_page_repository.GetById(id);
        StaticPageDocument post = static_page_model.item;

        // Make sure that the post not is null
        if (post == null)
        {
            // Return an error response
            return Json(data: new ResponseData(false, "", String.Format(tt.Get("error_no_post_found"), id)));
        }

        // Check for images to delete
        IList<string> images = collection["txtImageSrc"].ToArray();
        foreach(string src in post.images)
        {
            if(images.Contains(src) == false)
            {
                // Delete the image
                await this.blob_storage_repository.Delete("media", src);
            }
        }

        // Set images
        post.images = new List<string>();
        foreach (string img in images)
        {
            post.images.Add(img);
        }

        // Get files
        IFormFileCollection files = collection.Files;
        for (int i = 0; i < files.Count; i++)
        {
            // Just continue if the file is empty
            if (files[i].Length == 0)
                continue;

            // Get the filename
            string filename = Guid.NewGuid().ToString() + Path.GetExtension(files[i].FileName);

            // Add the blob
            await this.blob_storage_repository.UploadFromStream("media", filename, files[i].OpenReadStream());

            // Add the image src to the list
            post.images.Add(filename);
        }

        // Add or update the post
        await this.static_page_repository.Upsert(post);

        // Return a success response
        return Json(data: new ResponseData(true, string.Join("|", post.images), String.Format(tt.Get("success_post_updated"), tt.Get("static_page") + " (" + post.id + ")")));

    } // End of the images method

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Administrator")]
    public async Task<IActionResult> delete(IFormCollection collection)
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Get translated texts
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get the id 
        string id = collection["id"].ToString();

        // Get the post
        ModelItem<StaticPageDocument> page_model = await this.static_page_repository.GetById(id);

        // Remove images
        foreach(string src in page_model.item.images)
        {
            // Delete the image
            await this.blob_storage_repository.Delete("media", src);
        }

        // Try to delete a post
        bool success = await this.static_page_repository.DeleteOnId(id);

        // Return an error response
        if (success == false)
        {
            return Json(data: new ResponseData(false, "", String.Format(tt.Get("error_delete_post"), tt.Get("static_page") + " (" + id + ")")));
        }

        // Return a success response
        return Json(data: new ResponseData(true, id.ToString(), String.Format(tt.Get("success_delete_post"), tt.Get("static_page") + " (" + id + ")")));

    } // End of the delete method

    #endregion

    #region Validation

    public async Task<ResponseData> verify_page_name(KeyStringList tt, string id = "", string page_name = "")
    {
        // Get the current domain
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

        // Make sure that there is a page_name
        if (page_name == "")
        {
            return new ResponseData(false, "", String.Format(tt.Get("error_field_required"), tt.Get("page_name")));
        }

        // Check for invalid characters
        if (DataValidation.CheckPageNameCharacters(page_name) == false)
        {
            return new ResponseData(false, "", String.Format(tt.Get("error_field_bad_chars"), tt.Get("page_name")));
        }

        // Get a static page on page name
        ModelItem<StaticPagePost> page_name_model = await this.static_page_repository.GetByPageName(page_name, current_domain.back_end_language_code);

        // Check if the page name exists already
        if (page_name_model.item != null && id != page_name_model.item.id)
        {
            return new ResponseData(false, "", String.Format(tt.Get("error_field_unique"), tt.Get("page_name")));
        }

        // Return a success response
        return new ResponseData(true, id, "");

    } // End of the verify_page_name method

    #endregion

    #region Ajax methods

    [HttpPost]
    public async Task<IActionResult> index_list(IFormCollection collection)
    {
        // Get objects
        WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);
        KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

        // Get the form data
        string kw = collection["kw"].ToString();
        string sf = collection["sf"].ToString();
        string so = collection["so"].ToString();
        Int32 pz = Convert.ToInt32(collection["pz"].ToString());
        string ct = collection["ct"].ToString();

        // Get posts by keywords in a search
        ModelPage<StaticPageMeta> static_pages_page = await this.static_page_repository.GetBySearch(kw, current_domain.back_end_language_code, sf, so, pz, ct);

        // Add data to the form
        ViewBag.CurrentDomain = current_domain;
        ViewBag.TranslatedTexts = tt;
        ViewBag.CultureInfo = Tools.GetCultureInfo(current_domain.back_end_language_code, current_domain.country_code);
        ViewBag.Items = static_pages_page.items;
        ViewBag.Continuation = static_pages_page.ct;

        // Return the partial view
        return PartialView("/Views/admin_static_pages/_index_list.cshtml");

    } // End of the index_list method

    #endregion

} // End of the class

View

This view is used to add and update static pages. The form is submitted with ajax, replace the input button with a input submit if you want to submit the form directly to the controller. Forms is posted to the url in the action attribute.

@{
    // Set the layout for the page
    Layout = "/Views/shared_admin/_standard_layout.cshtml";

    // Get form data
    WebDomain current_domain = ViewBag.CurrentDomain;
    KeyStringList tt = ViewBag.TranslatedTexts;
    Int32 authorizationLevel = ViewBag.AuthorizationLevel;
    StaticPageDocument post = ViewBag.Post;
    StaticPageDetail spd = post.GetTranslation(current_domain.back_end_language_code);

    // Set texts
    string static_page_tt = tt.Get("static_page");
    string new_tt = tt.Get("new");
    string edit_tt = tt.Get("edit");
    string id_tt = tt.Get("id");
    string connection_tt = tt.Get("connection");
    string no_connection_tt = tt.Get("no_connection");
    string start_page_tt = tt.Get("start_page");
    string error_tt = tt.Get("error");
    string terms_of_service_tt = tt.Get("terms_of_service");
    string privacy_policy_tt = tt.Get("privacy_policy");
    string about_us_tt = tt.Get("about_us");
    string contact_us_tt = tt.Get("contact_us");
    string api_tt = tt.Get("api");
    string standards_tt = tt.Get("standards");
    string editor_tt = tt.Get("editor");
    string blog_tt = tt.Get("blog");
    string validate_tt = tt.Get("validate");
    string show_as_page_tt = tt.Get("show_as_page");
    string keywords_tt = tt.Get("keywords");
    string page_name_tt = tt.Get("page_name");
    string linkname_tt = tt.Get("linkname");
    string title_tt = tt.Get("title");
    string text_html_tt = tt.Get("text_html");
    string meta_description_tt = tt.Get("meta_description");
    string meta_keywords_tt = tt.Get("meta_keywords");
    string meta_robots_tt = tt.Get("meta_robots");
    string save_tt = tt.Get("save");
    string cancel_tt = tt.Get("cancel");

    string main_image_tt = tt.Get("main_image");
    string images_tt = tt.Get("images");
    string upload_tt = tt.Get("upload");
    string delete_tt = tt.Get("delete");


    // Set the title for the page
    if (post.translations.Count == 0)
    {
        ViewBag.Title = static_page_tt + " - " + new_tt.ToLower();
    }
    else
    {
        ViewBag.Title = static_page_tt + " - " + edit_tt.ToLower();
    }
}

@*Title*@
<h1>@ViewBag.Title</h1>

@*Menu*@
@await Html.PartialAsync("/Views/admin_static_pages/_form_menu.cshtml")

@*Main form*@
<form id="inputForm" action="/admin_static_pages/edit" method="post" enctype="multipart/form-data">

    @*Hidden data*@
    <input id="authorizationLevel" type="hidden" value="@authorizationLevel.ToString()" />
    <input id="errorNotUnique" type="hidden" value="@tt.Get("error_field_unique")" />
    <input id="errorNotAuthorized" type="hidden" value="@tt.Get("error_not_authorized")" />
    @Html.AntiForgeryToken()

    @*Input form*@
    <div class="annytab-form-label">@id_tt</div>
    <input id="txtId" name="txtId" type="text" class="annytab-form-control" tabindex="-1" readonly value="@post.id" />

    <div class="annytab-form-label">@connection_tt</div>
    <select id="selectConnection" name="selectConnection" class="annytab-form-control">
        <!option value="0" @(post.connection_id == 0 ? "selected" : "")>@no_connection_tt</!option>
        <!option value="1" @(post.connection_id == 1 ? "selected" : "")>@start_page_tt</!option>
        <!option value="2" @(post.connection_id == 2 ? "selected" : "")>@error_tt</!option>
        <!option value="3" @(post.connection_id == 3 ? "selected" : "")>@terms_of_service_tt</!option>
        <!option value="4" @(post.connection_id == 4 ? "selected" : "")>@privacy_policy_tt</!option>
        <!option value="5" @(post.connection_id == 5 ? "selected" : "")>@about_us_tt</!option>
        <!option value="6" @(post.connection_id == 6 ? "selected" : "")>@contact_us_tt</!option>
        <!option value="7" @(post.connection_id == 7 ? "selected" : "")>@api_tt</!option>
        <!option value="8" @(post.connection_id == 8 ? "selected" : "")>@standards_tt</!option>
        <!option value="9" @(post.connection_id == 9 ? "selected" : "")>@editor_tt</!option>
        <!option value="10" @(post.connection_id == 10 ? "selected" : "")>@blog_tt</!option>
        <!option value="11" @(post.connection_id == 11 ? "selected" : "")>@validate_tt</!option>
    </select>

    <div class="annytab-form-label">@meta_robots_tt</div>
    <select name="selectMetaRobots" class="annytab-form-control">
        <!option value="index, follow" @(post.meta_robots == "index, follow" ? "selected" : "")>index, follow</!option>
        <!option value="index, nofollow" @(post.meta_robots == "index, nofollow" ? "selected" : "")>index, nofollow</!option>
        <!option value="noindex, follow" @(post.meta_robots == "noindex, follow" ? "selected" : "")>noindex, follow</!option>
        <!option value="noindex, nofollow" @(post.meta_robots == "noindex, nofollow" ? "selected" : "")>noindex, nofollow</!option>
    </select>

    <div class="annytab-form-label">@show_as_page_tt</div>
    <div class="annytab-input-group">
        <div class="input-group-cell" style="width:40px;border-right:1px solid #d9d9d9;">
            <input name="cbShowAsPage" type="checkbox" class="annytab-form-checkbox" value="1" @(post != null && post.show_as_page == 1 ? "checked" : "") />
        </div>
        <div class="input-group-cell" style="width:1200px;text-align:left;padding-left:5px;">
            <div class="input-group-control">@show_as_page_tt</div>
        </div>
    </div>

    <div class="annytab-form-label">@page_name_tt</div>
    <input name="txtPageName" type="text" class="annytab-form-control" value="@post.page_name" data-val="true" data-val-required="@String.Format(tt.Get("error_field_required"), page_name_tt)" />
    <div class="field-validation-valid" data-valmsg-for="txtPageName" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@main_image_tt</div>
    <input name="txtMainImage" type="text" class="annytab-form-control" value="@post.main_image" />

    <div class="annytab-form-label">@keywords_tt</div>
    <textarea name="txtKeywords" class="annytab-form-control" rows="3">@string.Join(",", post.keywords)</textarea>
    <div class="field-validation-valid" data-valmsg-for="txtKeywords" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@linkname_tt</div>
    <input name="txtLinkName" type="text" class="annytab-form-control" value="@spd.link_name" data-val="true" data-val-required="@String.Format(tt.Get("error_field_required"), linkname_tt)" />
    <div class="field-validation-valid" data-valmsg-for="txtLinkName" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@title_tt</div>
    <input name="txtTitle" type="text" class="annytab-form-control" value="@spd.title" data-val="true" data-val-required="@String.Format(tt.Get("error_field_required"), title_tt)" />
    <div class="field-validation-valid" data-valmsg-for="txtTitle" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@text_html_tt</div>
    <textarea name="txtTextHtml" class="annytab-form-control" rows="10">@spd.text_html</textarea>
    <div class="field-validation-valid" data-valmsg-for="txtTextHtml" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@meta_description_tt</div>
    <textarea name="txtMetaDescription" class="annytab-form-control" rows="5">@spd.meta_description</textarea>
    <div class="field-validation-valid" data-valmsg-for="txtMetaDescription" data-valmsg-replace="true"></div>

    <div class="annytab-form-label">@meta_keywords_tt</div>
    <textarea name="txtMetaKeywords" class="annytab-form-control" rows="5">@spd.meta_keywords</textarea>
    <div class="field-validation-valid" data-valmsg-for="txtMetaKeywords" data-valmsg-replace="true"></div>

    <div class="annytab-basic-space"></div>

    @*Button panel*@
    <div class="annytab-form-button-container">
        <input type="button" class="annytab-form-button" value="@save_tt" onclick="submitForm($('#inputForm'))" />
        <a href="/admin_static_pages" class="annytab-form-button">@cancel_tt</a>
    </div>

</form>

<form id="imageForm" action="/admin_static_pages/images" method="post" enctype="multipart/form-data">

    @*Hidden data*@
    <input name="txtId" type="hidden" value="@post.id" />
    @Html.AntiForgeryToken()

    @*Input form*@
    <div class="annytab-form-label">@images_tt</div>
    <input id="fuImages" name="fuImages" type="file" class="annytab-form-control" multiple />

    <div id="imageContainer">
        @foreach (string src in post.images)
        {
            <div style="margin-top:20px;">
                <img src="@("https://mysite.blob.core.windows.net/media/" + src)" class="annytab-form-image" /><br />
                <span class="annytab-basic-bread-text">@("https://mysite.blob.core.windows.net/media/" + src) - <i class="far fa-trash-alt annytab-basic-link remove-image"></i></span>
                <input name="txtImageSrc" type="hidden" value="@src" />
            </div>
        }
    </div>

    <div class="annytab-basic-space"></div>

    @*Button panel*@
    <div class="annytab-form-button-container">
        <input type="button" class="annytab-form-button" value="@save_tt" onclick="uploadImages($('#imageForm'))" />
    </div>

</form>

@section scripts {
    <script type="text/javascript">

        // Set default focus
        $("#selectConnection").focus();

        $(document).on('click', '.remove-image', function () {
            $(this).parent().parent().remove();
        });

        // Submit the form
        function uploadImages(form)
        {
            // Get form data
            var form_data = new FormData(form[0]);

            $.ajax({
                type: 'POST',
                url: form.attr('action'),
                dataType: 'json',
                data: form_data,
                processData: false,
                contentType: false,
                success: function (data) {
                    if (data.success === true) {
                        $('#imageContainer').html('');
                        var images = data.id != "" ? data.id.split('|') : null;
                        for (var i in images)
                        {
                            $('#imageContainer').append('<div style="margin-top:20px;"><img src="https://mysite.blob.core.windows.net/media/'
                                + images[i] + '" class="annytab-form-image" /><br /><span class="annytab-basic-bread-text">https://mysite.blob.core.windows.net/media/'
                                + images[i] + ' - <i class="far fa-trash-alt annytab-basic-link remove-image"></i></span><input name="txtImageSrc" type="hidden" value="'
                                + images[i] + '" /></div>');
                        }
                        toastr['success'](data.message);
                    }
                    else
                    {
                        toastr['error'](data.message);
                    }
                    $('#fuImages').val('');
                }
            });

        } // End of the uploadImages method
    </script>
}

Submit a form without ajax

To submit a form without ajax/javascript you need to have a submit button in the form. The form is posted to the url in the action attribute.

<input type="submit" class="form-button" value="Save" />

Submit a form with ajax

To submit a form with ajax we need a button with an onclick event and a method to post the form to the action url. You might want to disable the button after a click to prevent the form from being posted multiple times.

<input type="button" class="annytab-form-button" value="Save" onclick="submitForm($('#inputForm'))" />
function submitForm(form)
{
    // Make sure that the form is valid
    if (form.valid() === false) { return false; }

    // Get form data
    var form_data = new FormData(form[0]);

    $.ajax({
        type: 'POST',
        url: form.attr('action'),
        dataType: 'json',
        data: form_data,
        processData: false,
        contentType: false,
        success: function (data) {
            if (data.success === true) {
                if (data.url !== "") {
                    location.href = data.url;
                }
                else {
                    toastr['success'](data.message);
                }
            }
            else {
                toastr['error'](data.message);
            }
        }
    });

} // End of the submitForm method

Leave a Reply

Your email address will not be published. Required fields are marked *