简体   繁体   中英

Pass Data from Partial View to Parent View

I have a Profile page with a Postcode look up feature from https://postcodes.io . So far I have a Partial View with Ajax Form using the following code.

@using (Ajax.BeginForm("_CityLookUp", "Home", new AjaxOptions
{
    HttpMethod = "POST",
    UpdateTargetId = "div1",
    InsertionMode = InsertionMode.Replace
}))
{
    <div id="div1">
        <div class="form-group">
            @Html.LabelFor(m => m.PostCode, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.PostCode, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
                <div class="col-md-10">
                    @Html.HiddenFor(m => m.County, new { @class = "form-control", id = "County" })
                </div>
            </div>
            <div class="form-group">
                <div class="col-md-10">
                    @Html.HiddenFor(m => m.City, new { @class = "form-control", id = "City" })
                </div>
            </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-primary" value="Look Up" />
            </div>
        </div>
    </div>
}

This works. I then rendered the Partial Page on the main profile page with the main profile form.

I just want to assign the value from the two other hidden fields to fields in the main page.

Partial Controller

    [HttpGet]
    public PartialViewResult _CityLookUp()
    {
        return PartialView();
    }

    [HttpPost]
    public PartialViewResult _CityLookUp(string postcode)
    {
        var client = new PostcodesIOClient();
        var result = client.Lookup(postcode);
        var jst = new AddressLookupViewModel();
        jst.City = result.AdminDistrict;
        jst.County = result.AdminCounty;
        jst.PostCode = postcode;
        ViewBag.City = jst.City;
        ViewBag.County = jst.County;

        var results = new BooksViewModel();
        results.City = jst.City;
        results.County = jst.County;

        return PartialView(jst);
    }

I have tried a new view model and assigning the results to the view model but it didn't work. Tried using JavaScript to get the value of the hidden field, no luck. If you'd rather do with a separate method, please explain how you would implement it.

Parent View

@model TestingView.Models.ParentViewModel
<div class="container">

    @Html.Partial("_CityLookUp")

    <div class="form-group">
        <div class="col-md-10">
            @Html.TextBoxFor(v => v.City, new { @class = "form-control", id = "City" })
        </div>
    </div>
</div> 

PartialViewModel

namespace TestingView.Models
{
    public class AddressLookupViewModel
    {
        public string PostCode { get; set; }
        public string County { get; set; }
        public string City { get; set; }

    }
}

Parent View Model

namespace TestingView.Models
{
    public class ParentViewModel
    {
        public string City { get; set; }
        public string County { get; set; }
    }
}

Side question: For some reason, when I hover over

@Html.HiddenFor(m => m.City, new { @class = "form-control", id = "City" })

on the parent view, it references the AddressLookUpViewModel and not the BooksViewModel in the parent view.. I have added both View Models.

I will answer this two ways; first using the approach you were initially asking about and then how I would prefer to implement this.

Solution 1

With this approach we will copy the value of the City element from the partial view to the parent view.

First thing we need to fix up is when you view the profile right now, there will be two elements with an Id of "City"; one from the parent page and one from the partial. That's invalid markup and it will cause problems for any potential solution.

Rename the Id attribute in the parent view:

@Html.TextBoxFor(v => v.City, new { @class = "form-control", id = "Profile_City" })

Update the partial view to call a js function when it successfully retreives a set of postcode values:

@using (Ajax.BeginForm("_CityLookUp", "Home", new AjaxOptions
{
    HttpMethod = "POST",
    UpdateTargetId = "div1",
    InsertionMode = InsertionMode.Replace,
    OnSuccess = "UpdateProfile" // Add this
}))

Add a js function, UpdateProfile, to the end of the parent view:

@section scripts {
    <script>
        function UpdateProfile()
        {
            var City = $("#City").val();
            alert(City);
            $("#Profile_City").val(City);
        }
    </script>
    }

That should be all that's required. The alert is just there for debugging.

The @section scripts code will be injected in to your _Layout.cshtml where it calls @RenderSection("scripts", required: false) , part of the default MVC project.

One problem that might crop up going forward is when you build the parent view into a form you might be tempted to nest the form elements for layout reasons but nested form elements aren't permitted.

Solution 2

This approach uses jQuery's ajax() method to fetch the data and directly populate the relevant fields on a form.

Set up the model.

namespace TestingView.Models
{
    public class ProfileViewModel
    {
        public string PostCode { get; set; }
        public string County { get; set; }
        public string City { get; set; }
    }
}

This is a copy of AddressLookupViewModel as it contains all the necessary fields. I have simply renamed it to suggest its use is for the main profile form itself.

Create the view.

The view now has a single Html.BeingForm() , with the Look Up button bound to an ajax function rather than submitting an ajax form.

Its not 100% clear to me whether you want the user to be able to edit the County and City fields after a look up. In the code below they can.

@model TestingView.Models.ProfileViewModel
@using (Html.BeginForm())
{
    <div class="form-group">
        @Html.LabelFor(m => m.PostCode, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.PostCode, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="button" class="btn btn-primary" onclick="Lookup()">Look Up</button>
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.County, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.County, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.City, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.City, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-primary" value="Submit" />
        </div>
    </div>
}

@section scripts {
    <script>
        function Lookup() {
            $.ajax("/Home/CityLookup", {
                type: "POST",
                dataType: 'json',
                data: {
                    postcode: $("#PostCode").val()
                }
            })
            .success(function (response) {
                console.log("Success");
                console.log(response);
                var cityObj = response;
                $("#City").val(cityObj.City);
                console.log("City " + cityObj.City);
                $("#County").val(cityObj.County);
            })
            .error(function () {
                alert("There was a problem looking up the postcode");
            })
        };
    </script>
    }

Create a controller to service the ajax request. This is heavily based on your POST _CityLookUp controller. Note the Action type is JsonResult and the return converts the jst object in to a JSON string. Using JSON makes the lookup extensible; we can return multiple properties as an object and unpack them to a javascript object for use client-side.

[HttpPost]
public JsonResult CityLookup(string postcode)
{
    var client = new PostcodesIOClient();
    var result = client.Lookup(postcode);
    var jst = new AddressLookupViewModel();
    jst.City = result.AdminDistrict;
    jst.County = result.AdminCounty;
    jst.PostCode = postcode;
    return Json(jst);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM