Search code examples
c#asp.net-coreasp.net-core-viewcomponent

Build ViewComponent in ViewModelBuilder


I want to keep the building of a view's model inside its respective controller's view model builder(referred to from here on as a vmb). Using HomeController as an example, I will instantiate from its constructor HomeViewModelBuilder.

public class HomeController : Controller
{
    private readonly HomeViewModelBuilder _viewModelBuilder;

    public HomeController(IUserManagerService userManagerService, IEmployeeService employeeService, IExampleServiceZ exampleServiceZ)
    {
       _viewModelBuilder = new HomeViewModelBuilder(userManagerService, employeeService, exampleServiceZ);
    }

    public ActionResult Index()
    {
        var model = _viewModelBuilder.BuildHomeViewModel(CurrentUserId);
        return View("Index", model);
    }
}

And to this vmb's constructor pass whatever services are needed to pull data from the repo.

Since .Net Core did not bring child actions along with it, I need to decide about how to continue with porting an asp.net mvc5 app. Some child action views call their own child actions. This isn't a question about nested view components, but rather, how to use a view component, keeping in mind that I want to do building in my builder. At the moment we are using a dirty hack with jquery like this:

The container on the view that will hold the child action

<div id="subsections-container">
    @*This is a place holder for the $.get*@
</div>

And at the bottom of the view is the script:

@section scripts{

    <script type="text/javascript">
        $(function () {
            $(document).ready(function() {
                var fieldsUrl = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Url.Action("Index", "SubSection")));

                $.get(fieldsUrl, { sectionId: @Model.Section.Id }, function (data) {
                    $("#subsections-container").html(data);
                });
            });
        });

    </script>
}

I would like to start using view components, but the example given by http://www.davepaquette.com/archive/2016/01/02/goodbye-child-actions-hello-view-components.aspx

Included here

namespace MyWebApplication.ViewComponents
{
    public class WhatsNewViewComponent : ViewComponent
    {
        private readonly IArticleService _articleService;

        public WhatsNewViewComponent(IArticleService articleService)
        {
            _articleService = articleService;
        }

        public IViewComponentResult Invoke(int numberOfItems)
        {
            var articles = _articleService.GetNewArticles(numberOfItems);
            return View(articles);
        }
    }
}

he is building inside the invoke of that view component. I want to do that in the vmb.

The second issue is that I have an ajax form that wants to refresh the data in that container.

Form in the view:

@using (@Ajax.BeginForm("Update", "SubSection", null, new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "subsections-container", OnBegin = "blockModal()", OnComplete = "hideModal();" }, new { id = "frm-subsection-update", @class = "form-horizontal" }))
{
}

The action hit in the subsection controller

[HttpPost]
[Authorize(Roles = RoleName.SubSectionUpdate)]
public ActionResult Update(SubsectionUpdateViewModel model)
{
    var subsection = new SubsectionDto();

    if (model.Id > 0)
        subsection = _subsectionService.Get(model.Id);

    subsection.InjectFrom(model);
    _subsectionService.Update(subsection);

    return RedirectToAction("Index", new { });
}

If this is no longer an action but a viewcomponent, what do I return here?


Solution

  • he is building inside the invoke of that view component. I want to do that in the vmb.

    Add WhatsNewViewComponentViewModel property in HomeViewModel and give responsibility of creating WhatsNewViewComponentViewModel to HomeViewModelBuilder

    In Index.cshtml of Home controller

    @await Component.InvokeAsync("WhatsNew",Model.WhatsNewViewComponentViewModel)
    

    WhatsNewViewComponent :

    public class WhatsNewViewComponent : ViewComponent
    {  
        public IViewComponentResult Invoke(WhatsNewViewComponentViewModel model)
        {
            return View(model);
        }
    }
    

    If this is no longer an action but a viewcomponent, what do I return here?

    Return ViewComponent from Action

    [HttpPost]
    [Authorize(Roles = RoleName.SubSectionUpdate)]
    public ActionResult Update(SubsectionUpdateViewModel model)
    {
        .......
        return ViewComponent("VCName", VCModel);
    }