3

目標

製品の動的リストを作成します。

シナリオ

製品を含むショッピング アプリケーションがあります。商品の をクリックしたときにadd button、追加した商品をサイドバーに表示したい。

要約された問題 (これを読む必要があります)

私はProductsSummary/Index.cshtml(Razor Engineを使用して)次のコードを持っています:

<ul class="summary">
    @if (Session["ProductsSummary"] == null)
    {
        <li class="is-empty">
            <p>Your summary is empty.</p>
        </li>
        <li data-bind="attr: { 'data-product-id': id }">
            <div class="product-summary-actions float-right">
                <button class="btn btn-danger btn-mini remove-item">
                    <i class="icon-remove"></i>
                </button>
            </div>
            <div class="product-summary-quantity">
                <h6 data-bind="text: infoComposition"></h6>
            </div>
            <div class="product-summary-description">
                <p data-bind="text: name"></p>
            </div>
        </li>
    }
    else
    {
        foreach 
          (var product in 
             (List<MyApp.Models.Data.getSpecificProductToShoppingList_Result>)
                Session["ProductsSummary"])
        {
        <!-- ko foreach: products -->
        <li data-product-id="@product.id">
            <div class="product-summary-actions float-right">
                <button class="btn btn-danger btn-mini remove-item">
                    <i class="icon-remove"></i>
                </button>
            </div>
            <div class="product-summary-quantity">
                <h6>
                   @product.quantity 
                   @product.measure@(@product.quantity == 1 ? "" : "s")
                </h6>
            </div>
            <div class="product-summary-description">
                <p>@product.name</p>
            </div>
        </li>
        <!-- /ko -->
        }
    }
</ul>

ご覧のとおり<li>、コードには 3 つあります。1 つ目は、「概要が空です」というメッセージを表示することです。セッションが null の場合 (もちろん、製品が追加された場合、Session は null です)。2 番目liは、セッションが null のときに何かを追加するときの Knockout のモデルとして機能します。最後に、セッションにある製品を表示します。

ここは少し「ドライな感じです。セッションが存在するかどうかに関係なく、同じテンプレートを再利用するにはどうすればよいですか?

詳細な問題

私はProductsSummary/Index.cshtml(Razor Engineを使用して)次のコードを持っています:

<ul class="summary">
    @if (Session["ProductsSummary"] == null)
    {
        <li class="is-empty">
            <p>Your summary is empty.</p>
        </li>
        <li data-bind="attr: { 'data-product-id': id }">
            <div class="product-summary-actions float-right">
                <button class="btn btn-danger btn-mini">
                    <i class="icon-remove"></i>
                </button>
            </div>
            <div class="product-summary-quantity">
                <h6 data-bind="text: infoComposition"></h6>
            </div>
            <div class="product-summary-description">
                <p data-bind="text: name"></p>
            </div>
        </li>
    }
    else
    {
        foreach 
          (var product in 
             (List<MyApp.Models.Data.getSpecificProductToShoppingList_Result>)
                Session["ProductsSummary"])
        {
        <!-- ko foreach: products -->
        <li data-product-id="@product.id">
            <div class="product-summary-actions float-right">
                <button class="btn btn-danger btn-mini remove-item">
                    <i class="icon-remove"></i>
                </button>
            </div>
            <div class="product-summary-quantity">
                <h6>
                   @product.quantity 
                   @product.measure@(@product.quantity == 1 ? "" : "s")
                </h6>
            </div>
            <div class="product-summary-description">
                <p>@product.name</p>
            </div>
        </li>
        <!-- ko -->
        }
    }
</ul>

ご覧のとおり、セッションが存在するifかどうかをチェックする があります。ProductsSummaryはいの場合、アプリケーションは、概要に追加した製品のリストを画面に表示します。

ご覧のとおり、セッションが nullliの場合、アプリケーションはwithis-emptyクラス内にメッセージを表示します。

ポイントは、要約に追加されたアイテムを表示するために「テンプレート」が本当に必要かということです。<li class="is-empty">[...]</li>

つまり、セッションの有無に関係なく、[製品の追加] ボタンをクリックしたときに Knockout に何かを表示する必要があることはわかっていますが、同様の目的で同じテンプレートを繰り返しています。

このフラグメントを見てください:

<li data-product-id="@product.id">
    <div class="product-summary-actions float-right">
        <button class="btn btn-danger btn-mini remove-item">
            <i class="icon-remove"></i>
        </button>
    </div>
    <div class="product-summary-quantity">
        <h6>
           @product.quantity 
           @product.measure@(@product.quantity == 1 ? "" : "s")
        </h6>
    </div>
    <div class="product-summary-description">
        <p>@product.name</p>
    </div>
</li>

この場合、foreachデータベースから取得したアイテムを表示する必要があるため、内部で使用しています。

一方、セッションがない場合、次のフラグメントが存在します。

<li data-bind="attr: { 'data-product-id': id }">
    <div class="product-summary-actions float-right">
        <button class="btn btn-danger btn-mini">
            <i class="icon-remove"></i>
        </button>
    </div>
    <div class="product-summary-quantity">
        <h6 data-bind="text: infoComposition"></h6>
    </div>
    <div class="product-summary-description">
        <p data-bind="text: name"></p>
    </div>
</li>

ご覧のとおり、両方のフラグメントは似ています — 1 つはデータベースからのデータを表し、もう 1 つはセッションがない場合に Knockout で動作するモデルをそれぞれ表します — これを「テンプレート化」する方法が必要です。

本当に必要なもの

  1. 誰かが私のサイト/アプリケーションに入ります。
  2. 私のレイアウトの右側には、「概要が空です。」というメッセージが表示されたサイドバーがあります。
  3. 「ああ、なんて素晴らしい製品でしょう! サマリーに追加します!」ユーザーAddは、「サマリーが空です」というボタンをクリックします。メッセージが消え、ユーザーが追加した製品がリストのアイテムの形式で表示されます ([最初/2 番目のフラグメント] の前に渡したのと同じテンプレート)。
  4. 「わかりました。今から別のカテゴリの製品に移動します。」— *ユーザーは「テレビ」カテゴリをクリックします* — 「なんてこった! このテレビを見てください! 今すぐ要約に追加します!」— *ユーザーが任意の TV の [追加] ボタンをクリックします。* — リストには既に製品がありましたが、別の (TV) が表示されました。
  5. 「ああ、気にしないでください。私はお金を持っていません。これらのアイテムを私の概要から削除します。」— *ユーザーはサマリーで各製品の「削除ボタン」をクリックします* — 製品がない場合、サマリーには「サマリーは空です」と表示されます。魔法のように、リフレッシュなどはありません。

(おかしいでしょ?)

KnockoutJS スクリプト

$(document).ready(function () {
    function Product(id, name, measure, quantity) {
        this.id = ko.observable(id);
        this.name = ko.observable(name);
        this.measure = ko.computed(function () {
            return quantity > 1 ? measure + "s" : measure;
        }, this);
        this.quantity = ko.observable(quantity);
        this.infoComposition = ko.computed(function () {
            return this.quantity() + " " + this.measure()
        }, this);
    }

    function SummaryViewModel() {
        this.products = ko.observableArray([]);

        this.addToSummary = function (formElement) {
            var $productId = $(formElement).children("[name=productId]").val();

            var match = $(".summary")
                           .find("li[data-product-id=" + $productId + "]").length;

            if (!match) {
                var $productName = 
                        $(formElement).children("[name=productName]").val(),
                    $productMeasure = 
                        $(formElement).children("[name=productMeasure]").val(),
                    $productQuantity = 
                        $(formElement).children("[name=productQuantity]").val();

                this.products.push
                   (new Product
                       ($productId, 
                        $productName, 
                        $productMeasure, 
                        $productQuantity));

                $.ajax({
                    type: "POST",
                    url: "/ProductsSummary/Add",
                    data: 
                       { 
                         productId: $productId, 
                         productQuantity: $productQuantity 
                       }
                });
            }
        }
    };

    var summaryViewModel = new SummaryViewModel();
    ko.applyBindings(summaryViewModel);

    $("body").on("click", ".remove-item", function () {
        summaryViewModel.products.remove(ko.dataFor(this));

        $.ajax({
            type: "POST",
            url: "/ProductsSummary/Remove",
            data: { productId: $(this).closest("li").data("product-id") }
        });
    });
});

結局、何が起こっているのですか?

私がやっていることは機能し、機能しません。技術的には、私のコードは機能しますが、繰り返したくありません。出来ますか?

技術的な詳細

サーバー側のチームはMVC 4 と Razor Engineを使用する C#.NET を使用し、クライアント側のチームはKnockoutJS と jQuery を使用します。

4

1 に答える 1

1

空のカート メッセージの場合は、次のようにすることができます。

<li class="is-empty" data-bind="visible: products().length < 1">
    <p>Your summary is empty.</p>
</li>

残りについては、これを実行できるはずです (MVC ループはありません)。

     <!-- ko foreach: products -->
     <li data-bind="attr: { 'data-product-id': id }">
        <div class="product-summary-actions float-right">
            <button class="btn btn-danger btn-mini remove-item">
                <i class="icon-remove"></i>
            </button>
        </div>
        <div class="product-summary-quantity">
            <h6 data-bind="text: infoComposition"></h6>
        </div>
        <div class="product-summary-description">
            <p data-bind="text: name"></p>
        </div>
    </li>
    <!-- /ko -->

セッションにアイテムが保存されている場合でも、クライアント側でリストを作成します。ビューで、既存のデータを JSON オブジェクトにシリアル化し、それをページの JavaScript 変数に保存します。この変数は、ドキュメントの準備ができたときに読み取って、観察可能な製品にプッシュできます。

var existingItems = @Html.Raw(Json.Encode((List<MyApp.Models.Data.getSpecificProductToShoppingList_Result>)Session["ProductsSummary"]));

$(document).ready(function() {
    // push existingItems into products observable.
});

私の構文は、JSON エンコーディングでは正しくない可能性があることに注意してください。

于 2013-06-25T21:04:15.583 に答える