0

バックグラウンド

グリッド付きのasp.netWebフォームがあり、ユーザーがそのグリッド内のテキストボックスを更新すると、onchangeイベントがWebMethod呼び出しを開始し、変更された行の残りを更新します。その時点では何も保存されていません。UIを更新しているだけです。

変更をコミットするには、[保存]ボタンをクリックします。

これは、ほとんどすべてのシナリオで実際に確実に機能します。しかし、私が解決できるはずだと感じている非常に永続的なものが1つありますが、それは専門家を呼ぶ時です。

問題のシナリオ

jQueryを使用してEnterキーをキャプチャしていますが、残念ながらそのイベントが最初に発生し、コールバックが完了する前にページが送信されます。行が正しく更新されていません。古くて当惑するようなデータが保存されます。

アップデート

行を変更せずに保存できるため、入力動作をコールバックに依存させることはできないと思います。その場合、行を変更しなければ、保存されることはありません。

さて、JavaScriptの内部リストを調べたり、自分で作成してなんらかの方法で管理したりする方法があれば、それはうまくいくでしょう。しかし、それは簡単なはずの何かのためのいくつかの重労働です。ですから、専門家が私に他のことを言わない限り、私はそれが間違っていると思い込まなければなりません。

試み

現在、組み込みのjQueryイベントを使用しており、この手の込んだsetTimeoutを使用して、保存が試行されたという事実を維持し、WebMethodが少なくとも呼び出されるのに十分な時間一時停止し、コールバックに依存して送信を行います。 。しかし、javascriptの非同期は私が期待したようには機能せず、onchangeイベントはそのコードのチャンクが完了するまで発生しません。それは驚くべきことでした。

自分の小さなオブジェクトを使用して、これらのイベントを正しい順序でキューに入れ、それをトリガーする賢い方法を見つけることができると考えていました。

これはすべて間違った方向のようです。確かにこれは非常識なやり過ぎです。これは一般的な問題であり、JavaScriptで24時間年中無休で作業していないため、簡単な解決策を見落としています。

右?

コード

これが私が今すぐ得たものです。これは明らかに機能しません-私はjqueryの非同期性を利用しようとしていましたが、行のonchangeイベントイベントが発生する前に、これらすべてを終了する必要があるようです。

$(document).bind("keypress", function (e) {
    if (e.keyCode == 13) {
        handleEnter();
        return false; //apparently I should be using e.preventDefault() here. 
    }
});


function handleEnter() {
    setTimeout(function () {
        if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
            alert('recalculating...');
            return true; //recur
        }

        //$('input[id$="ButtonSave"]').click();
        alert('no longer recalculating. click!');
        return false;
    }, 1000);
}

そして、典型的な行は次のようになります。これをバインドするためにjqueryを使用していないことに注意してください:

 <input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />

recalculateOrderItemRowのコードを投稿することもできますが、これは非常に長く、現在の問題は、キーが押された後のイベントが終了するまで起動しないことです。

更新の必要性ニック・フィッツジェラルド によると(そして人はクールな記事です)、setTimeoutを使用するとこれが非同期になるはずです。setTimeoutとjQueryの間の相互作用、および通常のjavascriptイベントとjQueryイベントの間の相互作用をさらに掘り下げます。

4

5 に答える 5

2

防ぐENTERことはあなたにそれほど問題を引き起こしてはいけません!コードに次のようなものがあることを確認してください。

$(document).on('keydown', 'input', function(e) {
    if(e.keyCode == 13) {
       e.preventDefault();
    }
});

アップデート

に保存したいようですENTERが、UIがで更新された後でのみchangeです。それは可能です。上記で提案したMatthewBlancarteのフラグを使用して、changeコールバックからsaveをトリガーし、を取り除くことができますsetTimeout

しかし、私はそれをお勧めしません。保存には、保存ボタンのみに依存することをお勧めします。そうしないと、ユーザーは2つの非同期操作が完了するのを待ってから、保存を完了する必要があります。したがって、UIをブロックするか、すべての非同期操作を追跡し、必要に応じて一部を中止する必要があります。ENTER保存に時間がかかりすぎると、ユーザーにとって直感的ではなくなります。

于 2012-04-12T03:42:39.500 に答える
2

以下の恐ろしい回避策の塊は、今日は一日中、昨日の半分は書くのに効果的にかかっていましたが、すべての順列を解決しているようです。

面白いことに、e.preventDefault()を呼び出した場合、enter自体はonchangeをトリガーしません。なぜだろう?変更は、保存ボタンをクリックするというデフォルトの動作が発生するまで実際には発生しません。

これについて他にほとんど面白いことはありません。

//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false; 
var saveOnID = '';

//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;

//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
    originalVal = this.value;
});

$(document).bind("keypress", function (e) {
    if (e.keyCode == 13) { //enter pressed.
        e.preventDefault();
        handleEnter();
    }
});

//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to 
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.  
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns. 
//Otherwise, we save as normal.
function handleEnter() {
    var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")

    //did we press enter with a field that triggers a callback selected?
    if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) { 
        //Set details so that the callback can know that we're saving.
        isSaving = true;
        saveOnID = focusedElement.attr('id');

        //Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
        focusedElement.trigger("change");
        focusedElement.focus();
    } else {
        forceSave();
    }
}

function isCallbackElement(element) {
    return (element.length == 1);
}

function isElementDirty(element) {
    if (element.length != 1) 
        return false;

    return (element.val() != originalVal);
}

function forceSave() {
    isSaving = false;
    saveOnID = '';

    $('input[id$="ButtonSave"]').click();
}

これは、テキストボックスの変更イベントで呼び出されます。

function recalculateOrderItemRow(textbox) {
    //I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.

    //Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
    PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
                                   orderItemDose,
                                   ProductItemSize,
                                   orderItemUnits,
                                   orderItemUnitPrice,
                                   onRecalculateOrderItemRowComplete);

}

次に、WebMethodコールバックコードの最後で、更新されたフォーム値を引き出し、jquery.caretを使用して必要な場所にキャレットを配置し、保存を強制する必要があるかどうかを確認します。

function onRecalculateOrderItemRowComplete(result) {
    var sender, row;

    sender = $('input[id="' + result.Sender + '"]');
    row = $(sender).closest('tr');

    row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
    row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
    row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));

    calculateGrandTotalPrice();
    $(document.activeElement).select();

    if (isSaving && saveOnID == result.Sender) {
        forceSave();
    }
}

result.Senderは、呼び出し側コントロールのIDであり、WebMethod呼び出しに詰め込んで、返しました。saveOnIDは完全ではない可能性があり、実際には、アクティブ/コールバックされていないWebMethod呼び出しのカウンターを維持して、保存前にすべてが終了することを完全に確認する方がよい場合があります。ふぅ。

于 2012-04-12T17:36:29.773 に答える
1

JavaScriptを投稿できますか?正しい方向に進んでいるようですね。OnChangeイベントを変更して、AJAX呼び出しを行う前に変数をインクリメントします。変数inProcessを呼び出して、ゼロに初期化します。AJAX呼び出しが戻ってきたら、inProcessを現在の値から1を引いた値に更新します。Enterキーイベントで、inProcessがゼロに等しいことを確認します。そうでない場合は、ユーザーに警告するか、タイムアウトを設定して少しで再試行することができます。

于 2012-04-12T02:48:03.003 に答える
1

onChangeイベント中にEnterキーキャプチャのバインドを解除し、コールバック関数の最後に再バインドすることができます。あなたがいくつかのコードを投稿するならば、私はより具体的な答えを与えることができます。

于 2012-04-12T03:27:01.240 に答える
0

WebMethodを非同期的に呼び出すべきではないようです。同期的に呼び出し、成功したらデータを保存します。

于 2012-04-12T03:20:04.850 に答える