【问题标题】:Bind Bootstrap datepicker with Knockout.js viewmodel (ASP.NET Core)将 Bootstrap 日期选择器与 Knockout.js 视图模型(ASP.NET Core)绑定
【发布时间】:2018-09-01 02:06:33
【问题描述】:

我正在尝试绑定 Bootstrap 日期选择器控件,但没有成功

<input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />

到一个 Knockout.js 视图模型。 (这是一个 ASP.NET Core 项目。)其中涉及的活动部件太多,以至于我无法在这种特定情况下隔离问题。 我想要实现的是一个简单的双向绑定:viewmodel 中的日期填充到 datepicker 控件中;并将表单提交的日期发送回视图模型/控制器。

我可以验证日期是否已成功传递给 Knockout 视图模型。为了跟踪它的价值,我目前正在将它作为文本打印在页面上:

<div>Viewmodel date as text: <span data-bind="text:Observation.ObservationDateTime"></span></div>

呈现如下:

我的研究表明,(1) 域模型、(2) 服务器端视图模型、(3) 客户端 (Knockout) 视图模型和 (4) 视图本身中的设置都很重要。所以我粘贴了下面每个的相关代码:

领域模型:

    [Required]
    //[Display(Name = "Date/Time")]
    //[DisplayFormat(DataFormatString = "{0:dddd, dd/MM/yyyy HH:mm}", ApplyFormatInEditMode = true)]
    [DataType(DataType.Date)]
    public DateTime ObservationDateTime { get; set; }

服务器视图模型/控制器:

Observation = new Observation() { ObservationDateTime = _systemClock.Now },

Client Knockout.js 视图模型

我尝试过各种绑定处理程序。目前(见位开始'ko.bindingHandlers.datepicker':

CreateObservationViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, observedSpeciesMapping, self);

    ko.bindingHandlers.selectPicker = {
        init: function (element, valueAccessor, allBindings) {
            $(element).selectpicker('render');
        }
    };

    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {};
            $(element).datepicker(options);

            //when a user changes the date, update the view model
            ko.utils.registerEventHandler(element, "changeDate", function (event) {
                var value = valueAccessor();
                if (ko.isObservable(value)) {
                    value(event.date);
                }
            });
        },
        update: function (element, valueAccessor) {
            var widget = $(element).data("datepicker");
            //when the view model is updated, update the widget
            if (widget) {
                widget.date = ko.utils.unwrapObservable(valueAccessor());
                if (widget.date) {
                    widget.setValue();
                }
            }
        }
    };

Razor 视图 datepicker Bootstrap 控件:

                <div class="form-group">
                    <label class="control-label" for="Observation.ObservationDateTime">Date:</label>
                    <input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />
                    <div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
                </div>

我认为问题出在 Knockout 视图模型中的 datepicker bindingHandler 上。但是,尽管进行了数小时的修补,我还没有解决它。有什么想法或建议吗?

更新 整个视图模型

CreateObservationViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, observedSpeciesMapping, self);

    ko.bindingHandlers.selectPicker = {
        init: function (element, valueAccessor, allBindings) {
            $(element).selectpicker('render');
        }
    };

    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {};
            $(element).datepicker(options);

            //when a user changes the date, update the view model
            ko.utils.registerEventHandler(element, "changeDate", function (event) {
                var value = valueAccessor();
                if (ko.isObservable(value)) {
                    value(event.date);
                }
            });
        },
        update: function (element, valueAccessor) {
            //when the view model is updated, update the widget
            var value = ko.unwrap(valueAccessor());
            $(element).val(value).datepicker("update");
        }
    };

    self.addObservedSpecies = function () {
        var observedSpecies = new ObservedSpeciesViewModel({ Id: 0, BirdId: 0, Quantity: 1 });
        self.ObservedSpecies.push(observedSpecies);
    };

    self.removeObservedSpecies = function () {
        if (self.ObservedSpecies().length > 1)
            self.ObservedSpecies.pop();
    };

    self.disableSubmitButton = ko.observable(false);

    self.Total = ko.computed(function () {
        var total = 0;
        total += self.ObservedSpecies().length;
        return total;
    }),


    self.post = function () {
        self.disableSubmitButton(true);
        if (self.ObservedSpecies().length < 1) {
            // ToDo: Implement proper client-side validation of the Observed Species collection
            alert("You must choose at least one observed bird species");
            self.MessageToClient("You must choose at least one observed bird species...");
            self.disableSubmitButton(false);
            return;
        }
        $.ajax({
            url: "/Observation/Post/",
            type: "POST",
            data: ko.toJSON(self),
            headers:
            {
                "content-type": "application/json; charset=utf-8"
            },
            success: function (data) {
                var obj = JSON.parse(data);
                if (obj.IsModelStateValid === false) {
                    self.MessageToClient(obj.MessageToClient);
                }
                else {
                    window.location.replace("./Index/");
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                self.disableSubmitButton(false);
                if (XMLHttpRequest.status === 400) {
                    $('#MessageToClient').text(XMLHttpRequest.responseText);
                }
                else {
                    $('#MessageToClient').text('The web server had an error.  The issue has been logged for investigation by the developer.');
                }
            }
        });
    };
};

var observedSpeciesMapping = {
    'ObservedSpecies': {
        key: function (obsevedSpecies) {
            return ko.utils.unwrapObservable(obsevedSpecies.Id);
        },
        create: function (options) {
            return new CreateObservationViewModel(options.data);
        }
    }
};

ObservedSpeciesViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, observedSpeciesMapping, self);  
};

剃刀视图 sn-p

@section scripts{
    <script src="~/js/knockout-3.4.2.js"></script>
    <script src="~/js/knockout.mapping-latest.js"></script>
    <script src="~/js/jqueryvalidate.js"></script>
    <script src="~/js/jquery-validate.bootstrap-tooltip.js"></script>
    <script src="~/js/createobservationviewmodel.js"></script>

    <script type="text/javascript">
    var createObservationViewModel = new CreateObservationViewModel(@Html.Raw(data));
    ko.applyBindings(createObservationViewModel);
    </script>
}

【问题讨论】:

    标签: asp.net-core knockout.js bootstrap-datepicker


    【解决方案1】:

    根据您的描述,我有点不确定哪个部分不起作用,但我可以看到两个问题。

    第一个问题非常小,可能只是转录错误;您在标记数据绑定中使用“datepicker”作为绑定定义和“datePicker”。绑定名称区分大小写。

    我认为第二个主要问题是日期选择器不知道您已更新其元素中的文本。为绑定的更新功能尝试这样的事情:

    update: function(element, valueAccessor) {
        //when the view model is updated, update the widget
        var value = ko.unwrap(valueAccessor());
        $(element).datepicker("update", new Date(value));
    }
    

    编辑:在绑定的工作版本中添加了 sn-p

    viewModel = function(data) {
      var self = this;
      self.Observation = {
        ObservationDateTime: ko.observable()
      };
    }
    
    ko.bindingHandlers.datepicker = {
      init: function(element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);
    
        //when a user changes the date, update the view model
        ko.utils.registerEventHandler(element, "changeDate", function(event) {
          var value = valueAccessor();
          if (ko.isObservable(value)) {
            value(event.date);
          }
        });
      },
      update: function(element, valueAccessor) {
        //when the view model is updated, update the widget
        var value = ko.unwrap(valueAccessor());
        $(element).datepicker("update", new Date(value));
      }
    };
    
    ko.applyBindings(new viewModel());
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/css/bootstrap-datepicker.min.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/js/bootstrap-datepicker.min.js"></script>
    
    
    <input type="text" class="form-control" data-bind="datepicker : Observation.ObservationDateTime" style="width: 120px; margin:8px;" />
    <div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
    <br/>
    <span>Manual change: </span><input type="text" data-bind="textInput: Observation.ObservationDateTime" />

    【讨论】:

    • 嗨,如果我将 HTML 更新为小写“datepicker”,我会收到 Uncaught TypeError: Uncaught TypeError: Unable to process binding "datepicker: function (){return Observation.ObservationDateTime }" 消息:$(.. .).datepicker 不是函数'
    • 问题是 datePicker 没有更新 viewmodel 日期值。
    • @Winthorpe selectPicker 还是 datePicker?我没有看到 selectPicker 在任何地方使用
    • 这里至少有一个工作版本的绑定。我不确定是否可以在不创建完整重现的情况下找出其他问题。 jsfiddle.net/jlspake/rspLx45y
    • 我已经设法使用您在小提琴中提供的解决方案使其正常工作。我还解决了加载 selectpicker.js 时的一些冲突。如果你想发布你的小提琴作为答案,我会这样标记它。否则我会接受你之前发布的答案。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 2012-11-13
    • 2017-09-21
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多