【问题标题】:Bootstrap modal dialog text fields don't update after I programmatically change the values until the user types in the field在我以编程方式更改值之后,引导模式对话框文本字段不会更新,直到用户在字段中键入
【发布时间】:2014-08-20 14:30:15
【问题描述】:

我遇到了以下问题:我有一个项目列表,用户可以单击其中的任何一个进行编辑。此时,将显示一个引导模式对话框,其中包含每个可编辑值的字段。我正在从底层 Knockout 视图模型中更新字段的值,因此当用户在模式对话框上编辑项目时,他们可以在后台看到正在修改的字段。因此,修改当前工作正常。但是,当对话框第一次打开时,它没有来自用户选择的项目的值;相反,它具有先前加载的值。但是,当用户开始编辑一个字段时,它会立即更新为应有的值,并允许用户继续编辑正确的字段。不知道这里发生了什么。

这是我的模态:

<script id="myModal" type="text/html">
          <div class="modal-header">
            <button type="button" class="close" data-bind="click: close" aria-hidden="true">&times;</button>
            <h3 data-bind="html: header"></h3>
          </div>
          <div class="modal-body">

                  <div class="form-group">
                    <label>First Name</label>
                    <input type="text" data-bind="value: modal.firstName, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Last Name</label>
                    <input type="text" data-bind="value: modal.lastName, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Phone</label>
                    <input type="text" data-bind="value: modal.phone, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Email</label>
                    <input type="text" data-bind="value: modal.email, valueUpdate: 'afterkeydown'" class="form-control" />
                </div>

          </div>
          <div class="modal-footer">
            <a href="#" class="btn" data-bind="click: close, html: closeLabel"></a>
            <a href="#" class="btn btn-primary" data-bind="click: action, html: primaryLabel"></a>
          </div>
  </script>


<!-- Create a modal via custom binding -->
        <div data-bind="bootstrapModal: modal" data-keyboard="false" data-backdrop="static"></div>

这是通过淘汰赛填充列表的部分:

<ul data-bind="foreach: tasks">
    <li>
        <div style="border:double">
            <div>
                <label data-bind="text: firstName"></label>
            </div>

            <div>
                <label data-bind="text: lastName"></label>
            </div>

            <div>
                <label data-bind="text: phone"></label>
            </div>

            <div>
                <label data-bind="text: email"></label>
            </div>

            <div>
                <button data-bind="click: editI.bind(this)">Edit</button>                            
                @*<button data-bind="click: $parent.removeUser">Delete</button>*@
            </div>
        </div>
    </li> 
</ul>

<form @*data-bind="submit: addUser"*@>
    <button type="submit">Add User</button>
</form>
<button @*data-bind="click: save"*@>Save</button>

这里是我设置模态值的地方,当敲除视图模型被加载时,它会起作用:

viewModel.modal = {
    header: ko.observable("This is a modal"),
    firstName: ko.observable("a"),
    lastName: ko.observable("a"),
    phone: ko.observable("a"),
    email: ko.observable("a"),
    body: ko.observable("test body"),
    closeLabel: "Close",
    primaryLabel: "Do Something",
    show: ko.observable(false), /* Set to true to show initially */
    onClose: function () {
        viewModel.onModalClose();
    },
    onAction: function () {
        viewModel.onModalAction();
    }

最后,这里是打开模式时调用的编辑函数。这就是事情出错的地方。项目元素被设置为模态视图模型,但在用户开始编辑该项目之前不会显示在模态中......然后,bam,项目显示在字段中。

    self.editI = function (item) {
        viewModel.modal.body = item.email;
        viewModel.modal.firstName = item.firstName;
        viewModel.modal.lastName = item.lastName;
        viewModel.modal.phone = item.phone;
        viewModel.modal.email = item.email;

        prevState = item;

        viewModel.modal.show(true);

注意:我发现了一些与此类似的 SO 帖子: how to destroy bootstrap modal window completely? 但他们并没有真正提供帮助。

【问题讨论】:

    标签: javascript jquery twitter-bootstrap knockout.js


    【解决方案1】:

    快速浏览后,您会发现您没有使用正确的语法将值分配给您的 observables。试试这个:

    self.editI = function (item) {
        viewModel.modal.email(item.email()); // item.email() OR ko.unwrap(item.email) if unsure whether it is an observable you're receiving
        viewModel.modal.firstName(item.firstName()); // item.firstName() OR ko.unwrap(item.firstName) if unsure whether it is an observable you're receiving
        viewModel.modal.lastName(item.lastName()); // item.lastName() OR ko.unwrap(item.lastName) if unsure whether it is an observable you're receiving
        viewModel.modal.phone(item.phone()); // item.phone() OR ko.unwrap(item.phone) if unsure whether it is an observable you're receiving
    
        prevState = item;
    
        viewModel.modal.show(true);
    

    编辑:可观察对象基本上是围绕变量的函数包装器。通过使用 = 分配它们的值,您删除了 observable 包装器,因为您重新分配了整个变量。通过使用()-syntax,您实际上调用了包装函数,它将处理 UI 更新并将新值分配给其内部变量。

    编辑 2:您可以通过使用链接使您的代码更简洁(至少在我看来):

    self.editI = function (item) {
        prevState = item;
        viewModel.modal
            .email(item.email())
            .firstName(item.firstName())
            .lastName(item.lastName())
            .phone(item.phone())
            .show(true);
    

    总结: 为了给 observable 一个新值并查看 ui 中的变化,请使用()

    var x = ko.observable(3);
    x(4); // UI will now reflect 4
    

    要从 observable 中获取基础值,请使用 ()ko.unwrap(如果变量是可观察的,也可以使用,这通常很有用)

    var x = ko.observable(3);
    console.log(x()); // 3
    console.log(ko.unwrap(x)); // 3
    console.log(ko.unwrap(3)); // 3, not giving any errors
    

    您需要将一个 observable 的值分配给另一个,因此您将两者结合起来:

    var x = ko.observable(3);
    var y = ko.observable(4);
    x(y()); // UI will reflect x = 4
    x(ko.unwrap(y)); // UI will reflect x = 4
    var z = 4;
    x(ko.unwrap(z)); // 4
    x(z()); // Error
    

    编辑 3:以简单的方式进行实时编辑(由于此答案下方的 cmets 而添加)。首先,对您的 HTML 模板进行一些更新(注意 with-binding)。

    <script id="myModal" type="text/html">
          <div class="modal-header">
            <button type="button" class="close" data-bind="click: close" aria-hidden="true">&times;</button>
            <h3 data-bind="html: header"></h3>
          </div>
          <div class="modal-body">
                <div class="form-group" data-bind="with: modal.item">
                    <label>First Name</label>
                    <input type="text" data-bind="value: firstName, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Last Name</label>
                    <input type="text" data-bind="value: lastName, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Phone</label>
                    <input type="text" data-bind="value: phone, valueUpdate: 'afterkeydown'" class="form-control" />
                    <label>Email</label>
                    <input type="text" data-bind="value: email, valueUpdate: 'afterkeydown'" class="form-control" />
                </div>
    
          </div>
          <div class="modal-footer">
            <a href="#" class="btn" data-bind="click: close, html: closeLabel"></a>
            <a href="#" class="btn btn-primary" data-bind="click: action, html: primaryLabel"></a>
          </div>
    </script>
    

    现在这个 modal.item 是什么?它基本上是您正在编辑的项目。不是副本,而是物品本身!这将为您提供实时编辑。但是,我们将项目放在为这种情况创建并用于 with-binding 的 observable item 中。看看我们在这里做什么?我们在模态模板中创建了绑定,为了不破坏它们,我们需要绑定一个将被我们的项目填充的可观察对象。这样我们就避免了您最初的问题。

    self.editI = function (item) {
        viewModel.modal.item(item); // viewModel.modal.item = ko.observable(null);    
        prevState = ko.toJS(item); // Because we are now editing 'item' directly, prevState will change along if we simply set it to item. So instead, I used ko.toJS to make a 'flat' copy.    
        viewModel.modal.show(true);
    

    可以通过将prevState 的属性复制回item 来“恢复”到以前的状态:

    self.restoreChanges = function () {
        var editingItem = viewModel.modal.item(); // Get the item we're editing out of the observable
        if (editingItem && prevState) {
            editingItem.email(prevState.email);
            editingItem.firstName(prevState.firstName);
            // Rest of properties
        }
    }
    

    【讨论】:

    • 好的。看起来是朝着正确方向迈出的一步。当我这样做时,我在每个文本框的模式对话框中得到类似以下内容: function c(){if(0
    • 对!我已经编辑了我的答案。我现在意识到您复制到编辑字段中的值已经是可观察的了。但是,您仍然不能使用直接赋值 (=),因为这样您仍然会重新分配变量并破坏您的绑定。您必须从原始 observable 中获取基础值(使用 ()ko.unwrap())并将其复制到其他 observable 中。详情见我的回答。
    • 在答案的末尾添加了一些一般性的解释,以防您对此感到困惑。
    • 这绝对解决了模态未正确填充的问题,但是,ko.unwrap() 似乎破坏了与视图模型的绑定,并且当我在字段中键入时它没有得到更新那个项目。有什么线索吗?
    • 我仍然遇到绑定损坏的问题。
    猜你喜欢
    • 2017-11-23
    • 2021-08-30
    • 2017-12-27
    • 2019-03-11
    • 2020-08-05
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 2015-08-02
    相关资源
    最近更新 更多