【问题标题】:Shopping Cart logic (?) trouble with KnockoutJSKnockoutJS 的购物车逻辑(?)问题
【发布时间】:2023-03-25 03:28:01
【问题描述】:

目标

制作动态产品列表。

情景

我有一个包含产品的购物应用程序。当我点击某个产品的add button 时,我想在侧边栏中显示我添加的产品。

总结的问题(你只需要阅读这个)

我的ProductsSummary/Index.cshtml 中有以下代码(使用 Razor 引擎):

<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>

如您所见,代码上有三个&lt;li&gt;。第一种是显示“摘要为空”的消息。如果会话为空(当然,如果添加了任何产品,则会话为空);第二个li 在会话为空时添加某些内容时用作淘汰赛的模型;最后一个是显示会话中的产品。

我感觉有点 "DRY" 就在这里。无论会话是否存在,如何重复使用相同的模板?

详细问题

我的ProductsSummary/Index.cshtml 中有以下代码(使用 Razor 引擎):

<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 会话是否存在。如果是,则应用程序会在屏幕上显示我在摘要中添加的产品列表。

如果会话为空,如您所见,应用程序在liis-empty 类中显示消息。

重点是:我真的需要&lt;li class="is-empty"&gt;[...]&lt;/li&gt; 之后的“模板”来显示添加到摘要中的项目?

我的意思是,我知道无论是否有会话,当我单击“添加产品”按钮时,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>

如您所见,这两个片段是相似的——一个代表来自数据库的数据,另一个代表分别在没有会话时与 Knockout 一起工作的模型——我需要一种“模板化”它的方法。

我真正需要的东西

  1. 有人进入我的网站/应用程序;
  2. 在我的布局右侧有一个带有消息的侧边栏:“摘要为空。”;
  3. “哦,多好的产品!我会把它添加到我的摘要中!”,然后用户点击Add按钮,“摘要为空”。消息消失,用户添加的产品以列表中项目的格式出现(与我在 [第一个/第二个片段] 之前传递的相同模板)。
  4. “好的,我现在就去另一类产品。” — *用户点击“电视”类别* —“哦,我的上帝!看看这台电视!我现在将添加到我的摘要中!” — *用户点击随机电视的“添加按钮”。* — 列表中已有产品,但出现了另一个(电视)。
  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") }
        });
    });
});

最终会发生什么?

我正在做的工作和不工作。从技术上讲,我的代码有效,但我不会重复它。有可能吗?

技术细节

服务器端团队使用 C#.NET 和 MVC 4 和 Razor 引擎,客户​​端团队使用 KnockoutJS 和 jQuery

【问题讨论】:

标签: c# javascript asp.net-mvc-4 razor knockout.js


【解决方案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 编码上可能不太正确。

【讨论】:

  • 你好,@JasonP!朋友,我们快到了!您的代码“完美”运行!但是,不幸的是,当我刷新页面时,会话的结果不存在。 =(
  • 好的,所以我们需要做一些调试。您是否确保页面第二次加载时,产品确实在会话中?如果是这样,您是否查看了页面的来源以确保现有的Items 对象是正确的?您的 javascript 控制台中是否有任何错误?
  • 你是对的,existingItems 没有被填充——它的值是null。您说:“请注意,我的语法在 JSON 编码上可能不太正确。” ——但我不明白:语法是对的!
  • 您是否确保产品列表此时确实在会话中?
  • 杰森,我已经解决了这个问题。您可以找到正确的语法here。问题是:我的 KnockoutJS 脚本位于 summary.js 上,并且该文件无法访问 @Html.Raw 语法。有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多