【问题标题】:Not sure how to manage my data in an observableArray for updating不确定如何在 observableArray 中管理我的数据以进行更新
【发布时间】:2012-08-25 14:27:44
【问题描述】:

我刚开始玩 KO,因为我想将它带入即将到来的项目中。在业余时间,我一直在工作中使用 Web 服务,这基本上是从对数据做一些事情开始,然后再做一些其他事情。所以首先我只是返回了一个药物名称列表。好的,让我们添加返回结果的计数。好的,让我们在模态中填充各个药物的详细信息。好的,让我们编辑一个药物名称。没有真正的韵律或理由,只是想出一些东西和玩 KO。

我现在不确定如何真正管理我的数据,因此它会随处更新。我制作了一个屏幕截图,引导您了解我所拥有的并说明问题。

http://i.imgur.com/5qNWQ.jpg

  1. 通过单击“y”按钮获取我的搜索结果。然后我选择 “Yasmin 28”药物
  2. 我在模态窗口中获得了该药物的详细视图
  3. 我编辑药品名称并点击“保存”按钮
  4. 药物名称在模态窗口中更新
  5. 我“作弊”并更新搜索结果中的药物名称 爬取 DOM 的那部分并将旧的药物名称替换为 新药名。

问题在于,它没有利用 KO 的可观察性...如果我关闭模态并再次单击“Yasmin 28”药物链接,模态将显示“Yasmin 28”不是我刚改成的(“美妙的东西”)。

我不太确定如何跟踪我的 observableArray 中的属性是否发生变化。我做了两个 observableArrays,一个保存搜索结果的药物名称列表,另一个保存药物的详细信息。我还为当前的药物名称做了一个 observable。

谁能解释我需要做什么才能在任何地方跟踪我的药物名称?我在最底部包含了下面的代码以及我正在使用的 JSON。

<div id="shell">
    <button class="load" value="j">j</button>
    <button class="load" value="k">k</button>
    <button class="load" value="x">x</button>
    <button class="load" value="y">y</button>
    <button class="load" value="z">z</button>
    <p id="loading"><img src="#{facesContext.externalContext.requestContextPath}/img/spinner.gif"/></p>

    <h3 data-bind="visible: drugList().length > 0"><span data-bind="text: count" class="count"></span> records returned</h3>

    <ul data-bind="foreach: drugList">
        <li>
            <span data-bind="text: drugName" class="results_drug_name"></span>
            <a data-bind="click: $root.showDetails" href="#" class="show">show details</a>
        </li>
    </ul>
</div>

<!-- start modal: drug details -->
<div id="dialog" data-bind="jqDialog: {autoOpen: false, title: drugName}">
    <p id="dialog_save_message" class="message_success">Changes saved successfully!!!!!!!!</p>

    <table data-bind="foreach: drugListDetails" class="table" width="100%" cellpadding="0" cellspacing="0" border="1">
        <tr>
            <th scope="row">pdlId</th>
            <td data-bind="text: pdlId"></td>
        </tr>
        <tr>
            <th scope="row">drugName</th>
            <td>
                <span data-bind="text: $root.drugName" class="readonly"></span>
                <input id="edit_drugname" class="edit_textfield" type="text" value="" size="35" />
                <button data-bind="click: $root.editSave" class="edit_buttons save">Save</button>
                <button data-bind="click: $root.editCancel" class="edit_buttons cancel">Cancel</button>
                <ul class="detail_actions">
                    <li><a data-bind="click: $root.edit" href="#" class="edit">edit</a></li>
                </ul>
            </td>
        </tr>

        <tr>
            <th scope="row">dosageFormDesc</th>
            <td data-bind="text: dosageFormDesc"></td>
        </tr>
        <tr>
            <th scope="row">strength</th>
            <td data-bind="text: strength"></td>
        </tr>
        <tr>
            <th scope="row">activeIngredient</th>
            <td data-bind="text: activeIngredient"></td>
        </tr>
        <tr>
            <th scope="row">tier</th>
            <td data-bind="text: tier"></td>
        </tr>
        <tr>
            <th scope="row">ancillaryCharge</th>
            <td data-bind="text: ancillaryCharge"></td>
        </tr>
        <tr>
            <th scope="row">preauthCode</th>
            <td data-bind="text: preauthCode"></td>
        </tr>
        <tr>
            <th scope="row">quantityLimit</th>
            <td data-bind="text: quantityLimit"></td>
        </tr>
        <tr>
            <th scope="row">prefAlternative</th>
            <td data-bind="text: prefAlternative"></td>
        </tr>
        <tr>
            <th scope="row">specialtyDrug</th>
            <td data-bind="text: specialtyDrug"></td>
        </tr>
        <tr>
            <th scope="row">partbCob</th>
            <td data-bind="text: partbCob"></td>
        </tr>
        <tr>
            <th scope="row">drugClassGroupId</th>
            <td data-bind="text: drugClassGroupId"></td>
        </tr>
        <tr>
            <th scope="row">drugClassId</th>
            <td data-bind="text: drugClassId"></td>
        </tr>
        <tr>
            <th scope="row">drugClass</th>
            <td data-bind="text: drugClass"></td>
        </tr>
        <tr>
            <th scope="row">genericInd</th>
            <td data-bind="text: genericInd"></td>
        </tr>
        <tr>
            <th scope="row">tip</th>
            <td data-bind="text: tip"></td>
        </tr>
    </table>
</div>
<!-- end modal: drug details -->

<script>
$(function() {
    $('.load').click(function() {
        var $letter = $(this).attr('value');

        //show spinner
        $('#loading').show();

        //load in drug list data
        $.getJSON('/PreferredDrugList/service/preferredDrugs/' + $letter, function(data) {
            //hide spinner
            $('#loading').hide();

            //replace drugList observableArray data
            //preferredDrugs is an array of objects, each elem is an individual drug
            myViewModel.drugList(data.preferredDrugs);

            //replace count observable data
            myViewModel.count(data.count);
        });//end getJSON
    });//end click

    //setup modal dialog options
    $('#dialog').dialog({
        autoOpen: false,
        closeOnEscape: true,
        modal: true,
        width:850,
        height:500
    });

});//end ondomready

//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
    init: function(element) {
       ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).dialog("destroy");
        });
    },
    update: function(element, valueAccessor) {
       var options = ko.toJS(valueAccessor());

        if (options) {
            $(element).dialog(options);
        }           
    }
};

var myViewModel = {
    count:                ko.observable(),        //# of records returned
    drugList:            ko.observableArray(),    //list of drug names - an array of objects
    drugListDetails:    ko.observableArray(),    //list of individual drug details
    drugName:            ko.observable(),        //current drug name

    //show drug details in modal
    //func gets passed the current observableArray elem (the individual drug info we clicked on, this is an object)
    showDetails: function(obj) {
        //replace current drug name observable data
        myViewModel.drugName(obj.drugName);

        //replace drugListDetails observableArray data, otherwise we'll append data to the modal
        myViewModel.drugListDetails([]);

        //push individual drug info to details observableArray
        myViewModel.drugListDetails.push(obj);

        //show dialog
        $('#dialog').dialog('open');

        return false;
    },

    //edit drug from modal
    edit: function(obj) {
        var $edit         = $('#dialog').find('td .edit'),
            $currentTD    = $edit.closest('td');

        $currentTD.addClass('editing');
        $currentTD.find('.readonly').hide();
        $currentTD.find('.edit_textfield').show().select();
        $currentTD.find('.edit_buttons').show();

        return false;
    },

    //save an edit
    editSave: function(obj) {
        alert('TODO save back to the server');

        var $saveBtn    = $('#dialog').find('td .save'),
            $currentTD    = $saveBtn.closest('td'),
            newDrugName = $('#edit_drugname').val(),
            $dialog_save_message = $('#dialog_save_message');

        //save new drug name to observable
        myViewModel.drugName(newDrugName);

        $currentTD.removeClass('editing');
        $currentTD.find('.readonly').show();
        $currentTD.find('.edit_textfield').hide();
        $currentTD.find('.edit_buttons').hide();

        $dialog_save_message.slideDown('slow', function() {
            //animation complete
            setTimeout(function() {
                $dialog_save_message.slideUp();
            }, 3000);
        });

        //cheat and update search results list with new drug name
        $('.results_drug_name').each(function(index, elem) {
            var $text = $(this).text();

            if ($text === obj.drugName) {
                $(this).text(newDrugName).addClass('edited');
            }
        });
    },

    //cancel an edit
    editCancel: function(obj) {
        var $cancelBtn     = $('#dialog').find('td .cancel'),
            $currentTD    = $cancelBtn.closest('td');

        $currentTD.removeClass('editing');
        $currentTD.find('.readonly').show();
        $currentTD.find('.edit_textfield').hide();
        $currentTD.find('.edit_buttons').hide();
    }
};

ko.applyBindings(myViewModel);

<!--what's returned from the web service-->
<pre>
{
    "preferredDrugs": [(8)
        {
            "pdlId": 8090,
            "drugName": "y-cof-dmx",
            "dosageFormDesc": "Liquid",
            "strength": "4MG/5ML; 15MG/5ML; 7.5MG/5ML",
            "activeIngredient": "BROMPHENIRAMINE MALEATE; DEXTROMETHORPHAN HYDROBROMIDE; PHENYLEPHRINE HYDROCHLORIDE",
            "tier": "OTC",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": null,
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 74,
            "drugClassId": 152,
            "drugClass": "Respiratory Tract Agents » Antitussives",
            "genericInd": "1",
            "tip": " "
        },-
        {
            "pdlId": 13417,
            "drugName": "YASMIN 28",
            "dosageFormDesc": "Tablet",
            "strength": "3MG; 0.03MG",
            "activeIngredient": "DROSPIRENONE; ETHINYL ESTRADIOL",
            "tier": "3",
            "ancillaryCharge": "AC",
            "preauthCode": " ",
            "quantityLimit": "28.0 tabs each 28 days",
            "prefAlternative": "ethinyl estradiol/drospirenone",
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 3,
            "drugClassId": 200,
            "drugClass": "Hormones and Synthetic Substitutes » Contraceptives",
            "genericInd": "0",
            "tip": " "
        },-
        {
            "pdlId": 24765,
            "drugName": "YAZ",
            "dosageFormDesc": "Tablet",
            "strength": "3MG; 0.02MG",
            "activeIngredient": "DROSPIRENONE; ETHINYL ESTRADIOL",
            "tier": "3",
            "ancillaryCharge": "AC",
            "preauthCode": " ",
            "quantityLimit": "28.0 tabs each 28 days",
            "prefAlternative": "ethinyl estradiol/drospirenone",
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 3,
            "drugClassId": 200,
            "drugClass": "Hormones and Synthetic Substitutes » Contraceptives",
            "genericInd": "0",
            "tip": " "
        },-
        {
            "pdlId": 2252,
            "drugName": "YERVOY",
            "dosageFormDesc": "Solution",
            "strength": "50MG/10ML",
            "activeIngredient": "IPILIMUMAB",
            "tier": "NC",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": null,
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 115,
            "drugClassId": 1,
            "drugClass": "Antineoplastic Agents",
            "genericInd": "0",
            "tip": " "
        },-
        {
            "pdlId": 20993,
            "drugName": "YERVOY",
            "dosageFormDesc": "Solution",
            "strength": "200MG/40ML",
            "activeIngredient": "IPILIMUMAB",
            "tier": "NC",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": null,
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 115,
            "drugClassId": 1,
            "drugClass": "Antineoplastic Agents",
            "genericInd": "0",
            "tip": " "
        },-
        {
            "pdlId": 564,
            "drugName": "YF-VAX",
            "dosageFormDesc": "Injection",
            "strength": "0",
            "activeIngredient": "YELLOW FEVER VACCINE",
            "tier": "NC",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": null,
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 79,
            "drugClassId": 284,
            "drugClass": "Serums, Toxoids and Vaccines » Vaccines",
            "genericInd": "0",
            "tip": " "
        },-
        {
            "pdlId": 8910,
            "drugName": "yodefan-nf chest congestion",
            "dosageFormDesc": "Liquid",
            "strength": "200MG/5ML",
            "activeIngredient": "GUAIFENESIN",
            "tier": "OTC",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": null,
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 84,
            "drugClassId": 155,
            "drugClass": "Respiratory Tract Agents » Expectorants",
            "genericInd": "1",
            "tip": " "
        },-
        {
            "pdlId": 13101,
            "drugName": "YODOXIN",
            "dosageFormDesc": "Tablet",
            "strength": "650MG",
            "activeIngredient": "IODOQUINOL",
            "tier": "3",
            "ancillaryCharge": "NA",
            "preauthCode": " ",
            "quantityLimit": " ",
            "prefAlternative": "iodoquinol",
            "specialtyDrug": " ",
            "partbCob": " ",
            "drugClassGroupId": 164,
            "drugClassId": 277,
            "drugClass": "Anti-infective Agents » Antiprotozoals",
            "genericInd": "0",
            "tip": " "
        }-
    ],-
    "count": 8
}   
</pre>

</script>

【问题讨论】:

  • 为什么要使用两个不同的药物清单?为什么不对两者使用相同的列表?
  • 这就是我想不通的——如何只使用一个 observableArray?当我从服务器加载 JSON 数据时,我正在使用:myViewModel.drugList(data.preferredDrugs); (使用 foreach 迭代的对象数组)我将如何使用它:myViewModel.drugList(data);我的 JSON 结构中哪个级别更高?我似乎无法深入研究 preferredDrugs 数组。

标签: data-binding knockout.js observable


【解决方案1】:

首先,删除 drugListDetails 数组,因为它会保留您的数据副本,从而导致混淆。

第二,使用ko.mapping.fromJSko.mapping.toJS 将您的data.preferredDrugs 映射到可观察数组或从可观察数组映射。这将允许knockout 跟踪数组中每个元素在 GUI 中的变化。这些文档可以在here找到。

如果您不想使用mapping 插件,也可以手动进行映射。

第三,在默认为falsedata.preferredDrugs 数组中的每个条目中添加一个新的showDetails 元素。这将在下一步中用于确定是否应显示对话框。调用现有的$root.showDetails 时,需要切换showDetails 元素。

第四,修改对话框div以使用drugList数组并将其visible值绑定到每个药物元素的showDetails值。这将创建多个隐藏对话框,因此您可能需要更改每个对话框的 id 值。

【讨论】:

  • 现在查看映射插件。我可以使用静态数据来实现它,但是当我从服务器加载 json 时它似乎不起作用。感谢该插件的提醒,看起来就像我需要的一样。
猜你喜欢
  • 2011-05-24
  • 2020-11-02
  • 2012-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 2014-07-18
  • 1970-01-01
相关资源
最近更新 更多