65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Knockout 和 AngularJS 实现级联下拉列表

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016年11月11日

CPOL

4分钟阅读

viewsIcon

14975

如何使用 Knockout 和 AngularJS 实现级联下拉列表。

引言

Knockout和AngularJS是两个流行的使用Model-View-ViewModel(MVVM)设计模式的JavaScript框架。 MVVM有三个组成部分。

模型(Model):服务器端的持久数据。
视图(View):HTML页面。
视图模型(ViewModel):一个表示模型的纯JavaScript对象。

模型和视图模型通常通过AJAX调用同步。 视图模型和视图由JavaScript框架(Knockout或AngularJS)绑定。 当视图元素更改时,视图模型由JavaScript框架自动更新。 当视图模型数据更改时,视图由框架自动更新。 框架会自动同步视图和视图模型,以节省开发人员编写这部分代码。

使用代码

服务器端ASP.NET MVC控制器

在本文中,服务器端使用ASP.NET MVC框架来返回下拉列表数据。

首先,创建两个用户类。

    public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
    }

    public class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int CategoryID { get; set; }
    }

其次,创建以下用户对象和MVC控制器方法,以返回客户端框架Knockout和AngularJS的数据。

        List<Category> lstCat = new List<Category>()
        {
            new Category() { CategoryID = 1, CategoryName = "Dairy" },
            new Category() { CategoryID = 2, CategoryName = "Meat" },
            new Category() { CategoryID = 3, CategoryName = "Vegetable" }
        };

        List<Product> lstProd = new List<Product>()
        {
            new Product() { ProductID = 1, ProductName = "Cheese", CategoryID = 1 },
            new Product() { ProductID = 2, ProductName = "Milk", CategoryID = 1 },
            new Product() { ProductID = 3, ProductName = "Yogurt", CategoryID = 1 },
            new Product() { ProductID = 4, ProductName = "Beef", CategoryID = 2 },
            new Product() { ProductID = 5, ProductName = "Lamb", CategoryID = 2 },
            new Product() { ProductID = 6, ProductName = "Pork", CategoryID = 2 },
            new Product() { ProductID = 7, ProductName = "Broccoli", CategoryID = 3 },
            new Product() { ProductID = 8, ProductName = "Cabbage", CategoryID = 3 },
            new Product() { ProductID = 9, ProductName = "Pepper", CategoryID = 3 }
        };

        public ActionResult GetCategories()
        {
            return Json(lstCat, JsonRequestBehavior.AllowGet);
        }

        public ActionResult GetProducts(int intCatID)
        {
            var products = lstProd.Where(p => p.CategoryID == intCatID);
            return Json(products, JsonRequestBehavior.AllowGet);
        }

Knockout实现级联下拉列表

首先,创建HTML标记,其中包含Knockout自定义属性,用于两个下拉列表。

<div>
    <label for="categoryList">Categories</label>
    <select id="categoryList" data-bind="options: categories,
                                            optionsText: 'CategoryName',
                                            optionsValue: 'CategoryID',
                                            value: selectedCategoryID,
                                            optionsCaption: 'Choose a category ...'">
    </select>
</div>
<div>
    <label for="productList">Products</label>
    <select id="productList" data-bind="options: products,
                                            optionsText: 'ProductName',
                                            optionsValue: 'ProductID',
                                            value: selectedProductID,
                                            optionsCaption: 'Choose a product ...'">
    </select>
</div>

注意select元素中的data-bind属性。 Knockout使用此属性将视图连接到视图模型。 这是select元素的绑定名称的解释。

options:用于选项标签的对象数组。
optionsText:选项标签的文本。
optionsValue:选项标签的值。
value:select标签的选中值。
optionsCaption:默认第一个选项的文本。 值为未定义。

其次,创建表示级联下拉列表对象的视图模型对象构造函数。

        function CascadeDdlViewModel() {
            this.categories = ko.observableArray();
            this.selectedCategoryID = ko.observable();
            this.products = ko.observableArray();
            this.selectedProductID = ko.observable();
        };

注意顶级Knockout对象ko的observableobservableArray函数。 函数observable使属性被观察到。 如果属性更改,则绑定到该属性的UI也会更改。 另一方面,如果绑定到该属性的UI更改,则该属性也会更改。 函数observableArray使数组属性被观察到。 请注意,它只使数组对象的添加和删除可观察。 它不会使对象内部的任何属性更改可观察。

第三,添加以下代码以实现级联下拉列表。

        $(document).ready(function () {
            vm = new CascadeDdlViewModel();
            ko.applyBindings(vm);

            // Subscribe to category dropdown list change.
            vm.selectedCategoryID.subscribe(function (newValue) {
                if (newValue !== undefined) {
                    $.getJSON('/home/GetProducts', { intCatID: newValue }, function (data) {
                        vm.products(data);
                    }).fail(function () {
                        alert('Error getting products!');
                    });
                }
                else {
                    vm.products.removeAll();
                    vm.selectedProductID(undefined);
                }
            });

            // Get a list of categories.
            $.getJSON('/home/GetCategories', null, function (data) {
                vm.categories(data);
            }).fail(function () {
                alert('Error getting categories!');
            });
        });

代码包装在jQuery DOM就绪函数中,因此在加载网页后调用该代码。 代码首先创建一个视图模型对象。 然后,它调用Knockout applyBindings函数来激活Knockout,以便它自动同步视图和视图模型。

为了获取第一个下拉列表更改的通知并重新填充第二个下拉列表,使用了显式订阅。 调用视图模型属性selectedCategoryIDsubscribe函数,以注册其对此观察属性的订阅。 当类别下拉列表选择更改时,绑定到下拉列表值的属性selectedCategoryID将更改。 当此属性的值更改时,将调用作为subscribe函数的参数传递的回调函数。 回调函数调用jQuery的getJSON函数以获取属于新类别的产品数据并更新products属性值。 当products属性值更改时,产品下拉列表的选项列表将更新。 Knockout会自动处理这一系列事件。

最后一段代码只是从服务器获取类别列表并填充类别下拉列表。

AngularJS实现级联下拉列表

首先,创建HTML标记,其中包含AngularJS指令,用于两个下拉列表。

<div id="ddlAppSection" ng-app="ddlApp" ng-controller="ddlController">
    <h2>Dropdown List Test</h2>
    <div>
        <label for="categoryList">Categories</label>
        <select id="categoryList"
            ng-model="selectedCategoryID"
            ng-options="c.CategoryID as c.CategoryName for c in categories">
        </select>
    </div>
    <div>
        Selected category: {{ selectedCategoryID }}
    </div>
    <div>
        <label for="productList">Products</label>
        <select id="productList"
            ng-model="selectedProductID"
            ng-options="p.ProductID as p.ProductName for p in products">
        </select>
    </div>
    <div>
        Selected product: {{ selectedProductID }}
    </div>
</div>

自定义属性(例如ng-app)是AngularJS指令。 这是此处使用的指令的解释。

ng-app:应用程序名称。
ng-controller:控制器名称。
ng-model:HTML元素绑定的对象属性名称。
ng-options:select标签内选项标签的数据。

{{}}语法用于AngularJS表达式。

其次,创建以下JavaScript代码以实现级联下拉列表。

        var ddlApp = angular.module('ddlApp', []);
        ddlApp.controller('ddlController', ['$scope', '$http', function ($scope, $http) {
            // Define model properties
            $scope.categories = [{ CategoryID: undefined, CategoryName: 'Choose a category ...' }];
            $scope.selectedCategoryID = undefined;
            $scope.products = [{ ProductID: undefined, ProductName: 'Choose a product ...'}];
            $scope.selectedProductID = undefined;

            // Get a list of categories
            $http.get('/home/GetCategories').then(function (response) {
                $scope.categories = response.data;
                $scope.categories.unshift({ CategoryID: undefined, CategoryName: 'Choose a category ...' });
            }, function (errResponse) {
                alert('Error getting categories!');
            });

            // Watch selected category value change and update products array.
            $scope.$watch('selectedCategoryID', function (newValue, oldValue) {
                if (newValue !== undefined) {
                    $http.get('/home/GetProducts', { params: { intCatID: newValue} }).then(function (response) {
                        $scope.products = response.data;
                        $scope.products.unshift({ ProductID: undefined, ProductName: 'Choose a product ...' });
                        $scope.selectedProductID = undefined;
                    }, function (errResponse) {
                        alert('Error getting products!');
                    });
                }
                else {
                    $scope.products = [{ ProductID: undefined, ProductName: 'Choose a product ...'}];
                    $scope.selectedProductID = undefined;
                }
            });
        } ]);

这段代码首先创建一个模块。 然后,它定义一个控制器。 控制器构造函数有两个参数$scope$http。 对象$scope是绑定到视图的视图模型。 对象$http是AngularJS服务,用于促进AJAX服务器调用。 方法$watch监听视图模型属性更改。 在这种情况下,当所选类别ID更改时,将调用$watch指定的函数。 这会根据新选择的类别ID更新产品下拉列表。 AngularJS自动处理视图模型和视图之间的同步。

关注点

Knockout和AngularJS都帮助Web开发人员创建和维护动态Web应用程序。 我更喜欢AngularJS,因为它拥有更大的开发人员社区。

 

 
© . All rights reserved.