title: ASP.NET Core 2.1 Unobtrusive Ajax Validation Not Working With Partial View Form Swap
body: <p>I've spent several hours combing Stackoverflow and other sites trying everyone's solutions with no luck so far.  I'm sure I've missed something, but I can't see it.  Hopefully you can point me to a fix.</p>
  
  <p>I have an initial form inside a partial view that is rendered into a parent view whose validation works fine.  Once the form is submitted via Ajax replace, I return either a login or registration partial view with a new form in the response.  This second form will not display the model validation errors when an incomplete form is submitted and the same partial view is returned.</p>
  
  <p>Thanks in advance for any tips you can offer to bring an end to this insanity!</p>
  
  <h2>Parent View Section</h2>
  
  <pre><code>&lt;div class="row"&gt;
  &lt;div class="col-sm-8 col-sm-offset-2"&gt;
      &lt;div class="panel panel-primary" id="formData"&gt;
          @await Html.PartialAsync("_UserNamePartial", new UserNameViewModel())
      &lt;/div&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <h2>Working Rendered Partial View</h2>
  
  <pre><code>&lt;div class="panel-heading"&gt;
  &lt;h3 class="panel-title"&gt;Let's Start With Your E-mail Address&lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="panel-body"&gt;
  &lt;div class="row"&gt;
      &lt;div class="col-xs-12"&gt;
          &lt;form asp-controller="Account" asp-action="IsAccountValid" data-ajax="true" data-ajax-method="POST"
                data-ajax-mode="replace" data-ajax-update="#formData"&gt;
              @Html.AntiForgeryToken()
              &lt;div class="form-group"&gt;
                  &lt;label for="UserName"&gt;Your Email Address&lt;/label&gt;
                  &lt;div class="input-group"&gt;
                      &lt;input type="text" id="UserName" name="UserName" class="form-control" placeholder="Your email address" /&gt;
                      &lt;div class="input-group-btn"&gt;
                          &lt;button type="submit" id="btnGetStarted" class="btn btn-primary"&gt;Get Started&lt;/button&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
                  &lt;span asp-validation-for="UserName" class="text-danger"&gt;&lt;/span&gt;
              &lt;/div&gt;
          &lt;/form&gt;
          &lt;div asp-validation-summary="ModelOnly" class="text-danger"&gt;&lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <h2>Initial Validation Controller Action</h2>
  
  <pre><code>    [HttpPost]
      [AllowAnonymous]
      [ValidateAntiForgeryToken]
      public IActionResult IsAccountValid(UserNameViewModel model)
      {
          if (!ModelState.IsValid)
              return PartialView("../Home/_UserNamePartial", model);
  
          AccountRepository accountRepository = new AccountRepository(ConnectionConfig.InshoraDev);
          AuthName match = accountRepository.GetAuthName(model.UserName);
  
          if (match != null)
          {
              ModelState.Clear();
              LoginViewModel loginModel = new LoginViewModel()
              {
                  UserName = model.UserName
              };
  
              return PartialView("_UserLoginPartial", loginModel);
          }
  
          ModelState.Clear();
          SignUpViewModel signupModel = new SignUpViewModel()
          {
              UserName = model.UserName,
          };
  
          return PartialView("_UserSignUp", signupModel);
      }
  </code></pre>
  
  <h2>Login Partial View (Validation Error Display Not Working)</h2>
  
  <pre><code>@model Inshora.Models.Account.LoginViewModel
  
  &lt;div asp-validation-summary="ModelOnly" class="text-danger"&gt;&lt;/div&gt;
  
  &lt;div class="panel-heading"&gt;
      &lt;h3 class="panel-title"&gt;Log Into Your Account&lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="panel-body"&gt;
      &lt;div class="row"&gt;
          &lt;div class="col-xs-12"&gt;
              &lt;form id="login-form" asp-controller="Account" asp-action="Login" method="post" role="form" style="display: block;"
                    data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="formData" data-ajax-complete="AcctLib.Login.Events.onComplete"&gt;
                  @Html.AntiForgeryToken()
                  &lt;div class="form-group"&gt;
                      &lt;input type="text" name="UserName" id="UserName" tabindex="1" class="form-control" placeholder="Email Address" value="@Model.UserName"&gt;
                      &lt;span asp-validation-for="UserName" class="text-danger"&gt;&lt;/span&gt;
                  &lt;/div&gt;
                  &lt;div class="form-group"&gt;
                      &lt;input type="password" name="Password" id="Password" tabindex="2" class="form-control" placeholder="Password"&gt;
                      &lt;span asp-validation-for="Password" class="text-danger"&gt;&lt;/span&gt;
                  &lt;/div&gt;
                  &lt;div class="form-group text-center"&gt;
                      &lt;input type="checkbox" tabindex="3" class="" name="RememberMe" id="RememberMe"&gt;
                      &lt;label for="RememberMe"&gt; Remember Me&lt;/label&gt;
                  &lt;/div&gt;
                  &lt;div class="form-group"&gt;
                      &lt;div class="row"&gt;
                          &lt;div class="col-sm-6 col-sm-offset-3"&gt;
                              &lt;input type="submit" name="login-submit" id="login-submit" tabindex="4" class="form-control btn btn-primary" value="Log In"&gt;
                          &lt;/div&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
                  &lt;div class="form-group"&gt;
                      &lt;div class="row"&gt;
                          &lt;div class="col-lg-12"&gt;
                              &lt;div class="text-center"&gt;
                                  &lt;a id="PasswordReset" asp-controller="Account" asp-action="PasswordReset" data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#formData" tabindex="5" class="inshora-forgot-password"&gt;Forgot Password?&lt;/a&gt;
                              &lt;/div&gt;
                          &lt;/div&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
              &lt;/form&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;script type="text/javascript"&gt;
      $(document).ready(function() {
          AcctLib.Login.Init();
      })
  &lt;/script&gt;
  </code></pre>
  
  <h2>LoginViewModel</h2>
  
  <pre><code>public class LoginViewModel
  {
      [Required]
      public string UserName { get; set; }
  
      [Required]
      public string Password { get; set; }
  
      [Required]
      public bool RememberMe { get; set; }
  }
  </code></pre>
  
  <h2>Client Side Initialization Code</h2>
  
  <pre><code>AcctLib.Login.RebindForm = function() {
      $('form').each(function (i, f) {
          $form = $(f);
          $form.removeData('validator');
          $form.removeData('unobtrusiveValidation');
          $.validator.unobtrusive.parse($form);
      });
  }
  
  AcctLib.Login.Init = function () {
      AcctLib.Login.RebindForm();
      $('#UserName').focus();
  }
  </code></pre>
  
  <h1>Update</h1>
  
  <p>I have updated the parent page (index.cshtml) to the following and it still doesn't display the messages.</p>
  
  <pre><code>&lt;div class="row"&gt;
      &lt;div class="col-sm-8 col-sm-offset-2"&gt;
          &lt;div class="panel panel-primary" id="formData"&gt;
              @await Html.PartialAsync("_UserNamePartial", new UserNameViewModel())
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  @section Scripts
  {
      @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
  }
  </code></pre>