Home > Blockchain >  RenderAction() to make decoupled components but how to communicate with other components
RenderAction() to make decoupled components but how to communicate with other components

Time:01-16

I have a busy view where most of the sections on there work off an ID. I'm looking for a more component way to handle each section so I'm using RenderAction() for each section where they have their own controllers. However I have a search section/"component" and when they put in a new Id and submit on that section/"component", I need a way for that to communicate to all the other RenderActions() that new Id so they can do their thing (query DB to get more info specific to that section).

My Search section would be something like:

    public class SearchController : Controller
    {
        [HttpGet]
        public ActionResult SearchContract()
        {
            var vm = new SearchVM();

            return PartialView(vm);
        }

        [HttpPost]
        public ActionResult SearchContract(SearchVM Search)
        {
            return PartialView(Search);
        }
    }

@using (Html.BeginForm())
{
<div >
    <div >Contract Id</div>
    <div >
        @Html.TextBoxFor(m => m.Id, new { @class = "form-control" })
    </div>
</div>

<input type="submit" />

}

Let's say ContractHeader is a section/"component" using RenderAction() that hits a different controller and method from the search:

public class ContractController : Controller
{
    
    public ActionResult ContractHeader(int ContractId)
    {
        // query contracts
        return PartialView(vm);
    }
}

Again, I'm looking for a more component oriented way with this. Yes it could all be in one controller but that's not what I'm looking for here. I want a more decoupled/compartmentalized approach to these areas on my views but trying to figure out how they can communicate with each other when "events" happen.

CodePudding user response:

I think I have it figured out. Basically on each search "component" (I'm calling components a separate controller and view that you use RenderAction() to get on your main view) the method that gets called when the search button is pressed will return the following code (I subclassed Controller and put tis method in)

    public ActionResult RedirectWithQueryString()
    {
        // get the referrer url without the old query string (which will be the main view)
        var uri = new Uri(Request.UrlReferrer.ToString());
        var url = Request.UrlReferrer.ToString().Replace(uri.Query, "");

        var allQS = System.Web.HttpUtility.ParseQueryString(uri.Query);

        var currentQS = System.Web.HttpUtility.ParseQueryString(Request.Url.Query);

        var combinedQS = new NameValueCollection();

        // update existing values
        foreach (var key in allQS.AllKeys)
        {
            combinedQS.Add(key, allQS[key]);
        }

        // add new values
        foreach (var key in currentQS.AllKeys)
        {
            if (combinedQS.AllKeys.Contains(key))
                combinedQS[key] = currentQS[key];
            else
                combinedQS.Add(key, currentQS[key]);
        }

        var finalUrl = url   combinedQS.ToQueryString();

        return Redirect(finalUrl);
    }



    public class ContractSearchController : MyBaseController
    {
        // GET: ContractSearch
        public ActionResult Index(ContractSearchVM model)
        {
            return PartialView("ContractSearch", model);
        }

        public ActionResult SearchContracts(ContractSearchVM model)
        {
            return RedirectWithQueryString();
        }
    }

    public class StopsSearchController : MyBaseController
    {
        public ActionResult Index(StopsSearchVM model)
        {
             // query to get some search related reference data like states list for drop down

            return PartialView("StopsSearch", model);
        }

        public ActionResult SearchStops(StopsSearchVM model)
        {
            return RedirectWithQueryString();
        }
    }

SearchContracts() and SearchStops() methods are called from their own forms in their own views using HttpGet. In those methods then we are provided with just that forms query string but we also can get the UrlReferrer query string which will have other search forms key/values in it. So RedirectWithQueryString() basically makes sure the final query string has ALL keys required to satisfy the model binding of any search components on the view and will update the given keys with the current value for the current search component that the submit button was on.

So this then causes us to refresh to the current view with all current key/values in query string for all search components which then is calling all the RenderActions() and the values can be passed.

@model FocusFridayComponents.Models.CombinedVM
<div >
<div >

    <div >
        <div >
            @{ Html.RenderAction("Index", "ContractSearch"); }
        </div>
    </div>
    <div >
        <div >
            @{ Html.RenderAction("Index", "StopsSearch"); }
        </div>
    </div>

</div>

<div >

    <!-- Contract Header -->
    <div >
        <div >

            @{ Html.RenderAction("Header", "ContractHeader", new { ContractId = Model.ContractSearch.Id }); }

        </div>
    </div>

    <!-- Contract Routes -->
    <div >
        <div >
            @* @{ Html.RenderAction("Index", "ContractRoutes", new { ContractId = Model.Id }); } *@
        </div>
    </div>

</div>

In the main view you're working on you just make a VM that combines the search VM's you're using on the view. The model binding will correctly map the query string keys that match the search VM's even when they are inside the combined VM. The catch here would be to make sure the keys/props of each search VM don't share the same names of any kind.

What's interesting is for the RenderAction() for contract and stops search I don't need to pass the model into it. The binding just does this automatically. For ContractHeader and ContractRoutes I am passing in a parameter because the idea is those are separate components and have their own input requirements and those can be named completely separate from any search models you may be using in your view so the binding wouldn't be able to map anything. This is a good thing though as it decouples your actual view components from your search components.

So you would do all of this to get components that are decoupled from each other but can still talk to each other and you can assemble your views and reuse a lot of these components by just gluing the RenderAction() parameters between them. This can help reduce giant monolithic VM's that tend to pop up on complex views you're making.

  • Related