简体   繁体   English

如何使用Knockout可观察数组来处理jQuery对话框

[英]How do you get a Knockout observable array to work with a jQuery dialog

I am new to javascript and knockout. 我是javascript和淘汰赛的新手。 Last week I spent all day trying to get a knockout observable to work with a jquery dialog for editing and adding new values to the array of items. 上周我花了一整天时间尝试使用jquery对话框进行淘汰观察,以便编辑和向项目数组添加新值。 I got SO close but in the end could not get it to work exactly. 我很接近,但最终无法让它完全正常工作。

My question has two parts. 我的问题有两个部分。

I have created a JSFiddle that perfectly re-creates my problem. 我创建了一个完美重新创建问题的JSFiddle。

This fiddle demonstrates that I can edit an existing discount code just fine. 这个小提琴表明我可以编辑现有的折扣代码。

I can also add a discount code. 我还可以添加折扣代码。 However when I add or edit a second (or third, etc) NEW discount code ALL of the NEW ones get edited. 但是,当我添加或编辑第二个(或第三个等)NEW折扣代码时,所有新的折扣代码都会被编辑。 It does add the new one but the first NEW one gets the same values as the second new item. 它确实添加了新的一个,但第一个新的获得与第二个新项目相同的值。

Here is the fiddle... 这是小提琴......

http://jsfiddle.net/sethspearman/2jxtpw7s/28/ http://jsfiddle.net/sethspearman/2jxtpw7s/28/

First Question. 第一个问题。 Can you get this fiddle to work? 你能让这个小提琴上班吗? I think the fix could be simple. 我认为修复可能很简单。 What do I need to change. 我需要改变什么。

Second Question. 第二个问题。 I know that this fiddle example is NOT making best usage of knockout bindings. 我知道这个小提琴的例子并没有充分利用敲除绑定。

For example, I am setting and then adding or editing using a vm.CurrentDiscountCode observable. 例如,我正在设置然后使用vm.CurrentDiscountCode observable添加或编辑。 However, I know that knockout has a $data binding context that could and should be used instead. 但是,我知道knockout有一个$ data绑定上下文,可以而且应该使用它。 I could never figure out how to get it to work. 我永远无法弄清楚如何让它发挥作用。 I figured out how to EDIT existing items using the $data context but could not figure out how to add new ones. 我想出了如何使用$ data上下文编辑现有项目,但无法弄清楚如何添加新项目。

So to sum it up. 总结一下。
What is the BARE minimum of change to get the fiddle working? 让小提琴工作的BARE最小变化是什么? And what is the OPTIMAL way to get this working? 什么是OPTIMAL方法来实现这个目标?

HERE IS THE CODE from the fiddle. 这是小提琴的代码。

EDIT Fiddle is now working. 编辑小提琴现在正在运作。 Did not work when using HTTPS. 使用HTTPS时无效。

HTML HTML

<body>
    <h1>Discount Codes Test</h1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-debug.js"></script>
<script id="editTmpl" type="text/html">
    <div>
        <ul>
            <li>
                <div class="inline-item">
                    <input type="hidden" name="codeId" id="codeId" data-bind="value: $parent.currentId" />
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Title</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="description" id="description" data-bind="value: $parent.currentDescription" />
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Code</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="discountCode" id="discountCode" data-bind="value: $root.currentCode, keypressvalidator: $root.currentCode, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentCode, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Discount Amount</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="amount" id="amount" data-bind="value: $root.currentDiscountAmount, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentDiscountAmount, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Max Usages</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="maxUsages" id="maxUsages" data-bind="value: $root.currentMaxUsages, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentMaxUsages, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Number of Usages</span>
                </div>
                <div class="inline-item">
                    <label data-bind="text: $parent.currentNumberOfUsages"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Active</span>
                </div>
                <div class="inline-item">
                    <input type="checkbox" name="isActive" id="isActive" data-bind="checked: $root.currentActive" />
                </div>
            </li>
        </ul>

        <button data-bind="jqButton: {}, click:$root.accept">Accept</button>
        <button data-bind="jqButton: {}, click:$root.cancel">Cancel</button>
    </div>
</script>    


<!--
**************************************
-->   
        <div>
            <fieldset data-regmode="printed">
                <div>
                    <h3>Add or Edit Discount Codes</h3>
                    <div id="discountCodes"></div>
                    <table>
                        <thead>
                            <tr>
                                <th>Title</th>
                                <th>Code</th>
                                <th>DiscountAmount</th>
                                <th>MaxUsages</th>
                                <th>Active</th>
                                <th>&nbsp;</th>
                            </tr>
                        </thead>
                        <tbody data-bind="foreach: DiscountCodes">
                            <tr style="cursor: pointer">
                                <td data-bind="text: Description"></td>
                                <td data-bind="text: Code"></td>
                                <td data-bind="text: DiscountAmount"></td>
                                <td data-bind="text: MaxUsages"></td>
                                <td data-bind="text: NumberOfUsages"></td>
                                <td data-bind="text: Active"></td>
                                <td><button type="button" data-bind="click: $root.editDiscountCode">Edit</button></td>
                            </tr>
                        </tbody>
                        <tfoot>
                            <tr> 
                                <td colspan="6"><button type="button" id="create-code" data-bind="click: createDiscountCode">Add New</button></td>
                            </tr>
                        </tfoot>
                    </table>
                </div>  
            </fieldset>
        </div>
        <div id="details" data-bind="jqDialog: { autoOpen: false, resizable: false, modal: true, height: 400, width:350, title:'Add/Edit Discount Code' }, template: { name: 'editTmpl', data: currentDiscountCode, if: currentDiscountCode }, openDialog: currentDiscountCode"></div>


</body>

JAVASCRIPT JAVASCRIPT

//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).dialog("destroy");
        });

        //dialog is moved to the bottom of the page by jQuery UI. Prevent initial pass of ko.applyBindings from hitting it
        setTimeout(function () {
            $(element).dialog(options);
        }, 0);
    }
};

var currentDialog;
//custom binding handler that opens/closes the dialog
ko.bindingHandlers.openDialog = {
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (typeof value == 'undefined') {
            return;
        }
        currentDialog = $(element);
        if (value) {
            $(element).dialog("open");
        } else {
            $(element).dialog("close");
        }
    }
};

//custom binding to initialize a jQuery UI button
ko.bindingHandlers.jqButton = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).button("destroy");
        });

        $(element).button(options);
    }
};

var vm = {};
var DiscountCode = function (description, code, discountAmount, maxUsages, numberOfUsages, active) {
    this.Id = ko.observable(0);
    this.Description = ko.observable(description)
    this.Code = ko.observable(code);
    this.DiscountAmount = ko.observable(discountAmount);
    this.MaxUsages = ko.observable(maxUsages);
    this.NumberOfUsages = ko.observable(numberOfUsages);
    this.Active = ko.observable(active);
}

var code1 = new DiscountCode('Give a 25% discount','DISC25',25, 5, 0, true); 
var code2 = new DiscountCode('Give a 10% discount','DISC10',10,20, 0, true); 
var code3 = new DiscountCode('Give a 5% discount' ,'DISC05', 5,30, 0, true); 

var discountCodes = ko.observableArray([code1,code2,code3]);

vm.DiscountCodes = discountCodes;

vm.currentId = ko.observable();
vm.currentDescription = ko.observable();
vm.currentCode = ko.observable().extend({ codeConverter: 0 });
vm.currentDiscountAmount = ko.observable();
vm.currentMaxUsages = ko.observable();
vm.currentNumberOfUsages = ko.observable();
vm.currentActive = ko.observable();

vm.currentDiscountCode = ko.observable();
vm.revertableDiscountCode = ko.observable();
vm.currentItemIsNew = ko.observable(false);

vm.editDiscountCode = function (discountCodeToEdit) {
    vm.revertableDiscountCode(discountCodeToEdit);
    vm.currentDiscountCode(discountCodeToEdit);
    vm.currentItemIsNew(false);
    setCurrent(discountCodeToEdit);
};

vm.createDiscountCode = function() {
    vm.currentDiscountCode(new DiscountCode("", "", 0, 0, 0, true));
    vm.currentItemIsNew(true);
    setCurrent(vm.currentDiscountCode());
};

function setCurrent(discountCode) {
    vm.currentId(discountCode.Id());
    vm.currentDescription (discountCode.Description());
    vm.currentCode(discountCode.Code());
    vm.currentDiscountAmount ( discountCode.DiscountAmount());
    vm.currentMaxUsages(discountCode.MaxUsages());
    vm.currentNumberOfUsages(discountCode.NumberOfUsages());
    vm.currentActive  (discountCode.Active());
}

vm.removeDiscountCode = function(discountCodeToRemove) {
    vm.DiscountCodes.remove(discountCodeToRemove);
}

vm.accept = function() {
    var currentItem = vm.currentDiscountCode();

    if (vm.currentItemIsNew()) {
        vm.DiscountCodes.push({
            Id: vm.currentId,
            Description: vm.currentDescription,
            Code: vm.currentCode,
            DiscountAmount: vm.currentDiscountAmount,
            MaxUsages: vm.currentMaxUsages,
            NumberOfUsages: vm.currentNumberOfUsages,
            Active: vm.currentActive
        });
        vm.currentItemIsNew(false);
    } else {
        currentItem.Id(vm.currentId());
        currentItem.Description(vm.currentDescription());
        currentItem.Code(vm.currentCode());
        currentItem.DiscountAmount(vm.currentDiscountAmount());
        currentItem.MaxUsages(vm.currentMaxUsages());
        currentItem.NumberOfUsages(vm.currentNumberOfUsages());
        currentItem.Active(vm.currentActive());
    }

    vm.currentDiscountCode("");
}

vm.cancel = function() {
    vm.currentDiscountCode(vm.revertableDiscountCode);
    currentDialog.dialog("close");
    vm.currentDiscountCode("");
}

ko.applyBindings(vm);

CSS CSS

body {
    font-size: 62.5%;
}

label, input {
    display: block;
}

    input.text {
        margin-bottom: 12px;
        width: 95%;
        padding: .4em;
    }

fieldset {
    padding: 0;
    border: 0;
    margin-top: 25px;
}

h1 {
    font-size: 1.2em;
    margin: .6em 0;
}

div#users-contain {
    width: 350px;
    margin: 20px 0;
}

    div#users-contain table {
        margin: 1em 0;
        border-collapse: collapse;
        width: 100%;
    }

        div#users-contain table td, div#users-contain table th {
            border: 1px solid #eee;
            padding: .6em 10px;
            text-align: left;
        }

.ui-dialog .ui-state-error {
    padding: .3em;
}

.validateTips {
    border: 1px solid transparent;
    padding: 0.3em;
}

Thanks in advance for your help. 在此先感谢您的帮助。

Seth 赛斯

In response to your question: "what is the OPTIMAL way to get this working?" 回答你的问题:“实现这项工作的最佳方式是什么?”

I would not start with custom binding handlers. 我不会从自定义绑定处理程序开始。 Start with a viewModel with well-defined methods (most of which you have). 从具有明确定义的方法的viewModel开始(大多数方法)。 In your methods, just manually execute the jquery to open and close the dialog. 在您的方法中,只需手动执行jquery即可打开和关闭对话框。 Get that working first. 获取工作第一。 Once you have it working, you can move it back into a bindingHandler if you think it's worthwhile. 一旦你有了它,你就可以把它移回一个bindingHandler,如果你认为它是值得的。

Changing this... 改变这个......

if (vm.currentItemIsNew()) {       
   vm.DiscountCodes.push({
        Id: vm.currentId,
        Description: vm.currentDescription,
        Code: vm.currentCode,
        DiscountAmount: vm.currentDiscountAmount,
        MaxUsages: vm.currentMaxUsages,
        NumberOfUsages: vm.currentNumberOfUsages,
        Active: vm.currentActive
    });

to this... 对...

if (vm.currentItemIsNew()) {
    vm.DiscountCodes.push(new DiscountCode(
        vm.currentDescription(),
        vm.currentCode(),
        vm.currentDiscountAmount(),
        vm.currentMaxUsages(),
        vm.currentNumberOfUsages(),
        vm.currentActive()
    ));

...did the trick. ......做了伎俩。
HT to Ryan Niemeyer (knockmeout.com) for the solution HT向Ryan Niemeyer(knockmeout.com)寻求解决方案

And to see a greatly simplified version... 并看到一个大大简化的版本......

http://jsfiddle.net/gh6mzrxp/34/ http://jsfiddle.net/gh6mzrxp/34/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM