Question

I´m making a form for registring personal data andjob experiences of a user. MVC5.

I have a Model that consists of a Clients and JobExperiences entities, created with EF code first

Over this model I created viewmodels, like this.

A view Model for the jobExperience

public class JobExperienceViewModel
{
    [Required]
    [Display(Name = "Nombre Compañia")]
    public string CompanyName { get; set; }

    [Required]
    [Display(Name = "Fecha de Ingreso")]
    [DataType(DataType.Date)]
    public DateTime EntryDate { get; set; }

    [Required]
    [Display(Name = "Fecha de Retiro")]
    [DataType(DataType.Date)]
    public DateTime RetirementDate { get; set; }


    [Required]
    [Display(Name = "Cargo")]
    public string Role { get; set; }

    [Required]
    [Display(Name = "Funciones")]
    public string Functions { get; set; }

    [Required]
    [Display(Name = "Motivo de Retiro")]
    public string RetirementCause { get; set; }
}

A Resume view model for saving the personal data and job experiences, like this public class ResumeViewModel {

    public ResumeViewModel(Client client, ApplicationUser user)
    {
        ...
    }


    public List<JobExperienceViewModel> JobExperienceViewModels { get; set; }

    [Display(Name = "Primer nombre")]
    public string FirstName { get; set; }

    [Display(Name = "Segundo nombre")]
    public string MiddleName { get; set; }

    [Display(Name = "Primer apellido")]
    public string LastName { get; set; }
}

I created a Resume Controller for orchesting all this data

public class ResumeController : Controller
{
    #region Global Actions

    //
    // GET: /Resume/
    //This method loads the information for a given userName and loads the viewmodel and pass it to the view
    [Authorize(Roles = "Admin, Recepcionista, Orientador")]
    public ActionResult Index(string userName)
    {
        var context = new ApplicationDbContext();
        var user = (from _user in context.Users
                    where _user.UserName == userName
                    select _user).FirstOrDefault();

        Client client = new Client();
        try
        {
            client = (from _client in context.Client
                      where _client.ApplicationUserId == user.Id
                      select _client).FirstOrDefault();

        }
        catch (System.Reflection.TargetException)
        {
            client = new Client();
        }

        ResumeViewModel model;

        model = new ResumeViewModel(client, user);

        TempData["ResumeViewModel"] = model;
        return View(model);
    }

    //This method is inttended to save all resume data to persistence
    [HttpPost]
    public ActionResult Index(ResumeViewModel model)
    {
        ApplicationDbContext context = new ApplicationDbContext();
        bool exists = false;

        var client = (from _client in context.Client
                      where _client.ApplicationUserId == model.ApplicationUserId
                      select _client).FirstOrDefault();

        if (!String.IsNullOrEmpty(client.ApplicationUserId))
        {
            exists = true;
        }

        client.Address = model.Address;
        client.ArmyCard = model.ArmyCard;
        client.ArmyCardClass = model.ArmyCardClass;
        client.BasicKnowledgeCoreId = model.SelectedBasicKnowledgeCoreId;
        client.BirthCityId = model.SelectedBirthCityId;
        client.BirthCountryId = model.SelectedBirthCountryId;
        client.BirthDate = model.BirthDate;
        client.BirthDepartmentId = model.SelectedBirthDepartmentId;
        client.CellPhoneNumber = model.CellPhoneNumber;
        client.Comments = model.Comments;
        client.DataVisibilityLevelId = model.SelectedDataVisibilityLevelId;
        client.DocumentNumber = model.DocumentNumber;
        client.DocumentTypeId = model.SelectedDocumentTypeId;
        client.DrivingLicenceExpirationDate = model.DrivingLicenceExpirationDate;
        client.DrivingLicenseCategoryId = model.SelectedDrivingLicenseCategoryId;
        client.EducationCountryId = model.SelectedEducationCountryId;
        client.FirstNationalityId = model.SelectedFirstNationalityId;
        client.GenreID = model.SelectedGenreId;
        client.GraduationYear = model.GraduationYear;
        client.HandicapTypeID = model.SelectedHandicapTypeID;
        client.HasWorkExperience = model.HasWorkExperience;
        client.HireTypeId = model.SelectedHireTypeId;
        client.HomeCityId = model.SelectedHomeCityId;
        client.HomeCountryId = model.SelectedHomeCountryId;
        client.HomeDepartmentId = model.SelectedHomeDepartmentId;
        client.Institution = model.Institution;
        client.IsGraduated = model.IsGraduated;
        client.IsHomeOfficeInterested = model.IsHomeOfficeInterested;
        client.IsHouseHoldHead = model.IsHouseHoldHead;
        client.LanguageId = model.SelectedLanguageId;
        client.LanguageSkillLevelID = model.SelectedLanguageSkillLevelID;
        client.MarriageStatusId = model.SelectedMarriageStatusId;
        client.Neighborhood = model.Neighborhood;
        client.PhoneNumber = model.PhoneNumber;
        client.ProCardExpeditionDate = model.ProCardExpeditionDate;
        client.ProCardNumber = model.ProCardNumber;
        client.ProfessionalCardCareer = model.ProfessionalCardCareer;
        client.ProfessionalProfile = model.ProfessionalProfile;
        client.SalaryRangeId = model.SelectedSalaryRangeId;
        //Este campo está comentadoporque el proceso de registro debe hacerse por detalles del nivel, donde esta elid y sus detalles.
        //client.ScholarLevelId = model.SelectedScholarLevelId;
        client.SecondNationalityId = model.SelectedSecondNationalityId;
        client.Title = model.Title;
        client.WorkStatusID = model.SelectedWorkStatusID;



        if (!exists)
        { context.Client.Add(client); }

        try
        {
            if (context.SaveChanges() > 0)
                RedirectToAction("Index", "SearchClient");
            else { return View(model); }
        }
        catch (Exception)
        {
            return View(model);
            throw;
        }

        return View(model);
    }

    #endregion

    #region JobExperience


    public ActionResult ShowJobExperience(ICollection<Models.JobExperienceViewModel> JobExperienceViewModels)
    {   
        return View(JobExperienceViewModels);
    }

    public ActionResult CreateJobExperience(ICollection<Models.JobExperienceViewModel> JobExperienceViewModels)
    {
        return View(JobExperienceViewModels);
    }

    [HttpGet]
    public ActionResult CreateJobExperience(Models.JobExperienceViewModel JobExperienceViewModel)
    {
        #region Using TempData

        ResumeViewModel model = (ResumeViewModel)TempData["ResumeViewModel"];

        model.JobExperienceViewModels.Add(JobExperienceViewModel);

        TempData["ResumeViewModel"] = model;

        #endregion

        return View("Index", model);
    }

    #endregion
}

And finally the views are organized this way. A parent view which contains all the personal data, and the jobExperiences list and create view as partial views within the big form.

SOme relevant portions ofthe view are

@model CAEWebSite.Models.ResumeViewModel

@{
    ViewBag.Title = "Hoja de Vida";
}

@using (Ajax.BeginForm(null))
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>
        @{var fullName = Model.FirstName +
                  (!String.IsNullOrWhiteSpace(Model.MiddleName) ? " " + Model.MiddleName : String.Empty) +
                  (!String.IsNullOrWhiteSpace(Model.LastName) ? " " + Model.LastName : String.Empty) +
                  (!String.IsNullOrWhiteSpace(Model.SecondLastName) ? " " + Model.SecondLastName : String.Empty);
        }
        @fullName
    </h4>
    <hr />
    @Html.ValidationSummary(true)

    <div class="form-group">
        <div class="col-md-12">
            @Model.Email
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-12">
            @Model.UserName
        </div>
    </div>
    <hr />
    <h3>Datos personales</h3>
    <div class="form-group">
        @Html.LabelFor(model => model.BirthDate, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.TextBoxFor(model => model.BirthDate, new { @type = "date" })

            @Html.ValidationMessageFor(model => model.BirthDate)
        </div>
    </div>

    <h3>Experiencia laboral</h3>
    <div class="form-group">
        @Html.LabelFor(model => model.HasWorkExperience, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.HasWorkExperience)
            @Html.ValidationMessageFor(model => model.HasWorkExperience)
        </div>
    </div>
    <h3>Trabajos anteriores</h3>
    <div class="col-md-12">
        <div id="divListJobExperience">
            @Html.Partial("_ListJobExperience", Model.JobExperienceViewModels)
        </div>
        <p>
            @*@Html.Action("ShowJobExperience", Model.JobExperienceViewModels)*@
            <a href="#" onclick="$('#newJobExperience').bPopup({}); return false;">Nueva experiencia</a>
        </p>
    </div>

<div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>

}

<div id="newJobExperience" style="background-color:#fff; border-radius:5px;">
<div class="col-md-12">
    @Html.Partial("_CreateJobExperience", new CAEWebSite.Models.JobExperienceViewModel())
</div>
</div>

</div>

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")

<script>
    $("#newJobExperience").hide();
    $("#newRelative").hide();
    $("#newCapacitationCourse").hide();
    $("#newScholarLevelDetail").hide();
</script>

}

The @Html.Partial("_CreateJobExperience", new CAEWebSite.Models.JobExperienceViewModel())

partial view is loaded with bpopup plugin as a modal. The partial views are like this

_ListJobExperience view

@model IEnumerable<CAEWebSite.Models.JobExperienceViewModel>


<table class="table">
<tr>
    <th>
        @Html.DisplayNameFor(model => model.CompanyName)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.EntryDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.RetirementDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Role)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Functions)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.RetirementCause)
    </th>
    <th></th>
</tr>

@foreach (var item in Model)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.CompanyName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.EntryDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.RetirementDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Role)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Functions)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.RetirementCause)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
            @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
            @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
        </td>
    </tr>
}

</table>

_CreateJobExperience View

public class ResumeController : Controller
{
    #region Global Actions

    //
    // GET: /Resume/
    [Authorize(Roles = "Admin, Recepcionista, Orientador")]
    public ActionResult Index(string userName)
    {
        var context = new ApplicationDbContext();
        var user = (from _user in context.Users
                    where _user.UserName == userName
                    select _user).FirstOrDefault();

        Client client = new Client();
        try
        {
            client = (from _client in context.Client
                      where _client.ApplicationUserId == user.Id
                      select _client).FirstOrDefault();

        }
        catch (System.Reflection.TargetException)
        {
            client = new Client();
        }

        ResumeViewModel model;

        model = new ResumeViewModel(client, user);

        TempData["ResumeViewModel"] = model;
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(ResumeViewModel model)
    {
        ApplicationDbContext context = new ApplicationDbContext();
        bool exists = false;

        var client = (from _client in context.Client
                      where _client.ApplicationUserId == model.ApplicationUserId
                      select _client).FirstOrDefault();

        if (!String.IsNullOrEmpty(client.ApplicationUserId))
        {
            exists = true;
        }

        client.Address = model.Address;
        client.ArmyCard = model.ArmyCard;
        client.ArmyCardClass = model.ArmyCardClass;
        client.BasicKnowledgeCoreId = model.SelectedBasicKnowledgeCoreId;
        client.BirthCityId = model.SelectedBirthCityId;
        client.BirthCountryId = model.SelectedBirthCountryId;
        client.BirthDate = model.BirthDate;
        client.BirthDepartmentId = model.SelectedBirthDepartmentId;
        client.CellPhoneNumber = model.CellPhoneNumber;
        client.Comments = model.Comments;
        client.DataVisibilityLevelId = model.SelectedDataVisibilityLevelId;
        client.DocumentNumber = model.DocumentNumber;
        client.DocumentTypeId = model.SelectedDocumentTypeId;
        client.DrivingLicenceExpirationDate = model.DrivingLicenceExpirationDate;
        client.DrivingLicenseCategoryId = model.SelectedDrivingLicenseCategoryId;
        client.EducationCountryId = model.SelectedEducationCountryId;
        client.FirstNationalityId = model.SelectedFirstNationalityId;
        client.GenreID = model.SelectedGenreId;
        client.GraduationYear = model.GraduationYear;
        client.HandicapTypeID = model.SelectedHandicapTypeID;
        client.HasWorkExperience = model.HasWorkExperience;
        client.HireTypeId = model.SelectedHireTypeId;
        client.HomeCityId = model.SelectedHomeCityId;
        client.HomeCountryId = model.SelectedHomeCountryId;
        client.HomeDepartmentId = model.SelectedHomeDepartmentId;
        client.Institution = model.Institution;
        client.IsGraduated = model.IsGraduated;
        client.IsHomeOfficeInterested = model.IsHomeOfficeInterested;
        client.IsHouseHoldHead = model.IsHouseHoldHead;
        client.LanguageId = model.SelectedLanguageId;
        client.LanguageSkillLevelID = model.SelectedLanguageSkillLevelID;
        client.MarriageStatusId = model.SelectedMarriageStatusId;
        client.Neighborhood = model.Neighborhood;
        client.PhoneNumber = model.PhoneNumber;
        client.ProCardExpeditionDate = model.ProCardExpeditionDate;
        client.ProCardNumber = model.ProCardNumber;
        client.ProfessionalCardCareer = model.ProfessionalCardCareer;
        client.ProfessionalProfile = model.ProfessionalProfile;
        client.SalaryRangeId = model.SelectedSalaryRangeId;
        //Este campo está comentadoporque el proceso de registro debe hacerse por detalles del nivel, donde esta elid y sus detalles.
        //client.ScholarLevelId = model.SelectedScholarLevelId;
        client.SecondNationalityId = model.SelectedSecondNationalityId;
        client.Title = model.Title;
        client.WorkStatusID = model.SelectedWorkStatusID;



        if (!exists)
        { context.Client.Add(client); }

        try
        {
            if (context.SaveChanges() > 0)
                RedirectToAction("Index", "SearchClient");
            else { return View(model); }
        }
        catch (Exception)
        {
            return View(model);
            throw;
        }

        return View(model);
    }

    #endregion

    #region JobExperience


    public ActionResult ShowJobExperience(ICollection<Models.JobExperienceViewModel> JobExperienceViewModels)
    {   
        return View(JobExperienceViewModels);
    }

    public ActionResult CreateJobExperience(ICollection<Models.JobExperienceViewModel> JobExperienceViewModels)
    {
        return View(JobExperienceViewModels);
    }

    [HttpPost]
    public ActionResult CreateJobExperience(Models.JobExperienceViewModel JobExperienceViewModel)
    {
        #region Using TempData

        ResumeViewModel model = (ResumeViewModel)TempData["ResumeViewModel"];

        model.JobExperienceViewModels.Add(JobExperienceViewModel);

        TempData["ResumeViewModel"] = model;

        #endregion

        return View("Index", model);
    }

    #endregion
}
}

The problem

Whenever I create a new job experience Iupdate the viemodel adding a jobExperienceViewModel to the list of experiences, and send it back to the view to be returned.

And it works, The list of experiences appears filled. however, all the data in the form got lost, cleaned.

I´m using the approach of keeping viewmodel state during all user interaction until he or she clicks the forms submit when I´ll send all the model to persistence.

I think what I need is a way to update the experiences list like it were inside an old fashioned Updatepanel.

Or a workaround that alows me to cover the functionality.

Was it helpful?

Solution

Here you would use an Ajax form to submit the laboral expiences without clean the data in "principal form":

Model:

    public class HojaDeVida
{
    public HojaDeVida()
    {
        ExperienciasLaborales = new List<ExperienciaLaboral>();
    }

    public string Nombre { get; set; }
    public string Apellido { get; set; }
    public IList<ExperienciaLaboral> ExperienciasLaborales { get; set; }
}

public class ExperienciaLaboral
{
    public string Empresa { get; set; }
    public int Anios { get; set; }
}

Controller:

    public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        TempData["hojavida"] = new HojaDeVida();
        return View();
    }

    [HttpPost]
    public ActionResult Index(HojaDeVida hojaDeVida)
    {
         //Submit the info in "hojaDeVida" and TempData["HojaVida"]
        return new EmptyResult();
    }

    [HttpPost]
    public PartialViewResult AddExperienciaLaboral(ExperienciaLaboral experiencia)
    {
        var hojadevida = (HojaDeVida)TempData["hojavida"];
        hojadevida.ExperienciasLaborales.Add(experiencia);
        TempData["hojavida"] = hojadevida;
        return PartialView("_ListExperiencias", hojadevida.ExperienciasLaborales);
    }
}

"Principal View" (Hoja de Vida):

@using WebApplication1.Controllers
@model HojaDeVida
@{
    ViewBag.Title = "Home Page";
}

@using (Html.BeginForm("Index", "Home"))
    {
        <div>
            @Html.LabelFor(x => x.Nombre)
            @Html.EditorFor(x => x.Nombre)
        </div>
        <div>
            @Html.LabelFor(x => x.Apellido)
            @Html.EditorFor(x => x.Apellido)
        </div>
        <input type="submit" value="Guardar Hoja de Vida" />
    }

@Html.Partial("_AddExperiencia", new ExperienciaLaboral())

@section scripts
{
    @Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
}

Then the "_AddExperience" PartialView, with an Ajax Form:

@using WebApplication1.Controllers
@model ExperienciaLaboral
@using (Ajax.BeginForm("AddExperienciaLaboral", "Home", new AjaxOptions { UpdateTargetId = "data", HttpMethod = "POST" }))
    {
        <div>
            @Html.LabelFor(x => Model.Empresa)
            @Html.EditorFor(x => Model.Empresa)
        </div>
        <div>
            @Html.LabelFor(x => Model.Anios)
            @Html.EditorFor(x => Model.Anios)
        </div>
        <input type="submit" value="Agregar Experiencia" />
    }
<div id="data">
    @Html.Partial("_ListExperiencias", new List<ExperienciaLaboral>())
</div>

And finally the "_ListExperiencias" PartialView:

@model IList<WebApplication1.Controllers.ExperienciaLaboral>
@foreach (var item in Model)
    {
        <div>
            @string.Format("Empresa : {0} | Años: {1}", item.Empresa, item.Anios)
    </div>
    }

As you can see, the Ajax Form is outside of the HTML Form, you can't nested two forms (Ajax Form and HTML Form here) instead you need work with some of JavaScript or the jQuery.Ajax to have a nice visual forms nested

OTHER TIPS

you can use ajax + web api or any action in the controller to save the experiences.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top