0

最近 WPF と MVVM を掘り下げたので、これについて間違った考えをしているのかもしれませんが、私の Web プロジェクトには次の ViewModel があります。

public class Route
{
    public Address Source {get; set;}
    public Address Destination {get; set;}
}

public class Address
{
    public string Text {get; set;}
    public Location Location {get; set;}
}

puclic class Location
{
    [Required]
    public double? Latitude {get; set;}
    [Required]
    public double? Longitude {get; set;}
}

public class Search
{
    public Route {get; set;}
    public IEnumerable<Route> Results {get; set;}
}

私は次のビューを持っています (_ 接頭辞付きのビューは部分的です)

   Home.cshtml - the main page, has @model Search with call to @Html.Partial("_Route", Model.Search) inside an Ajax.BeginForm.

   _Route.cshtml - @model Route, with two Html.Partials for _Address, one for Model.Source and one for Model.Destination

   _Address.cshtml - @model.Address, with a text box which should be "bound" to the Address.Text property.

私が何をしようとしているのかが明確でない場合 - ユーザーにソースボックスと宛先ボックスに検索テキストを入力させ、結果を取得させようとしています。Source ボックスと Destination ボックスは、Ajax を介してオートコンプリートし、ユーザーがオートコンプリートの選択肢の 1 つを選択すると、Address.Location.Latitude と Address.Location.Longitude を設定する必要があります。

最後に、ユーザーが有効な送信元と送信先の場所を取得したら、Home.cshtml の [送信] ボタンを Ajax 経由で呼び出して、検索結果を取得し、グリッドにバインドする必要があります。

かなり標準的なものだと思います...

次のことを達成するための「最もクリーンな」方法に苦労しています。

  1. Location は実際には Address ビューのどこにも表示されない (テキストのみ) ため、ASP.NET MVC の DataAnnotations 検証を有効にして検証を支援するにはどうすればよいでしょうか?

  2. ユーザーが AutoComplete の選択肢の 1 つを選択したときに、Ajax 呼び出しを介して Route オブジェクトを POST する必要がある Home.cshtml に、Location の値を戻すにはどうすればよいですか?

  3. ソースと宛先でユーザーが選択したアドレスの「状態」を維持するにはどうすればよいですか? オートコンプリートに Select ハンドラーを追加しますか? しかし、その後、選択した値をどうしますか?

これが明確であることを願っています...しかし、そうでない場合はお知らせください。明確にするよう努めます。

4

1 に答える 1

1

jQuery UI autocompleteを使用できます。例を見てみましょう。

次のモデルを使用できます。

public class Route
{
    public Address Source { get; set; }
    public Address Destination { get; set; }
}

public class Address
{
    [Required]
    [ValidCity("Location", ErrorMessage = "Please select a valid city")]
    public string Text { get; set; }
    public Location Location { get; set; }
}

public class Location
{
    public double? Latitude { get; set; }
    public double? Longitude { get; set; }
}

public class Search
{
    public Route RouteSearch { get; set; }
    public IEnumerable<Route> Results { get; set; }
}

[ValidCity]Address クラスで使用したカスタム バリデータは次のとおりです。

public class ValidCityAttribute : ValidationAttribute
{
    private readonly string _locationPropertyName;
    public ValidCityAttribute(string locationPropertyName)
    {
        _locationPropertyName = locationPropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_locationPropertyName);
        if (property == null)
        {
            return new ValidationResult(string.Format("Unknown property: {0}", _locationPropertyName));
        }
        var location = (Location)property.GetValue(validationContext.ObjectInstance, null);
        var city = value as string;

        if (!string.IsNullOrEmpty(city) && location != null && location.Latitude.HasValue && location.Longitude.HasValue)
        {
            // TODO: at this stage we have a city name with corresponding 
            // latitude, longitude => we could validate if they match
            // right now we suppose they are valid

            return null;
        }

        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }
}

次にコントローラー:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new Search
        {
            RouteSearch = new Route
            {
                Source = new Address(),
                Destination = new Address()
            }
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(Route search)
    {
        if (!ModelState.IsValid)
        {
            return PartialView("_Route", search);
        }

        // TODO: do the search here and return the results:

        var lat1 = Math.PI * search.Source.Location.Latitude.Value / 180;
        var lon1 = Math.PI * search.Source.Location.Longitude.Value / 180;

        var lat2 = Math.PI * search.Destination.Location.Latitude.Value / 180;
        var lon2 = Math.PI * search.Destination.Location.Longitude.Value / 180;

        var R = 6371;
        var distance = Math.Acos(Math.Sin(lat1) * Math.Sin(lat2) +
                          Math.Cos(lat1) * Math.Cos(lat2) *
                          Math.Cos(lon2 - lon1)) * R;
        return Json(new { distance = distance });
    }

    public ActionResult GetLocation(string text)
    {
        var results = new[]
        {
            new { city = "Paris", latitude = 48.8567, longitude = 2.3508 },
            new { city = "London", latitude = 51.507222, longitude = -0.1275 },
            new { city = "Madrid", latitude = 40.4, longitude = -3.683333 },
            new { city = "Berlin", latitude = 52.500556, longitude = 13.398889 },
        }.Where(x => x.city.IndexOf(text, StringComparison.OrdinalIgnoreCase) > -1);

        return Json(results, JsonRequestBehavior.AllowGet);
    }
}

対応する~/Views/Home/Index.cshtmlビュー:

@model Search

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "searchComplete" }))
{
    <div id="search">
        @Html.Partial("_Route", Model.RouteSearch)
    </div>
    <p><button type="submit">OK</button></p>
}

~/Views/Home/_Route.cshtmlパーシャル:

@model ToDD.Controllers.Route

<h3>Source</h3>
@Html.EditorFor(x => x.Source)

<h3>Destination</h3>
@Html.EditorFor(x => x.Destination)

Address クラスのエディター テンプレート ( ~/Views/Home/EditorTemplates/Address.cshtml):

@model Address

<span class="address">
    @Html.TextBoxFor(x => x.Text, new { data_url = Url.Action("GetLocation") })
    @Html.ValidationMessageFor(x => x.Text)
    @Html.HiddenFor(x => x.Location.Latitude, new { @class = "lat" })
    @Html.HiddenFor(x => x.Location.Longitude, new { @class = "lon" })
</span>

最後の部分は、すべてを生き生きとさせることです。まず、次のスクリプトを含めます (使用している jQuery と jQuery UI のバージョンを調整してください)。

<script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.20.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>

最後に、カスタム スクリプトを記述してすべてを接続します。

var searchComplete = function (result) {
    if (result.distance) {
        alert('the distance is ' + result.distance);
    } else {
        $('#search').html(result);
        attachAutoComplete();
    }
};

var attachAutoComplete = function () {
    $('.address :text').autocomplete({
        source: function (request, response) {
            $.ajax({
                url: $(this.element).data('url'),
                type: 'GET',
                cache: false,
                data: { text: request.term },
                context: response,
                success: function (result) {
                    this($.map(result, function (item) {
                        return {
                            label: item.city,
                            value: item.city,
                            latitude: item.latitude,
                            longitude: item.longitude
                        };
                    }));
                }
            });
        },
        select: function (event, ui) {
            var address = $(this).closest('.address');
            address.find('.lat').val(ui.item.latitude);
            address.find('.lon').val(ui.item.longitude);
        },
        minLength: 2
    }).change(function () {
        var address = $(this).closest('.address');
        address.find('.lat').val('');
        address.find('.lon').val('');
    });
}

$(document).ready(attachAutoComplete);
于 2012-08-21T12:24:31.660 に答える