1

MVCプロジェクトでノックアウト(KO)を使用しています。サーバー上に(グリッド用の)MVCモデルを作成し、それをビューに渡します。ビューでは、シリアル化されてKOモデルに変換され(ko.mappingを使用)、バインドに使用されます。そのバインディングは、グリッド作成のためにHTMLで使用されます。

これは私のMVCグリッドモデルがどのように見えるかであり、ko.mappingによって対応するKOモデルに変換されます。

public class GridModel
    {
        /// <summary>
        /// Grid body for the grid.
        /// </summary>
        public GridBodyModel GridBodyModel { get; set; }

        /// <summary>
        /// Grid context.
        /// </summary>
        public GridContext GridContext { get; set; }

        /// <summary>
        /// Grid header for the grid.
        /// </summary>
        public GridHeaderModel GridHeader { get; set; }
    }

public class GridBodyModel
    {
        /// <summary>
        /// List of grid body rows.
        /// </summary>
        public IList<GridRowModel> Rows { get; set; }
    }


public class GridContext
    {
        /// <summary>
        /// Total number of pages. Read-only.
        /// </summary>
        public int TotalPages{ get; set; }
    }

public class GridHeaderModel
    {
        /// <summary>
        /// List of grid header cells.
        /// </summary>
        public IList<GridHeaderCellModel> Cells { get; set; }
    }

明らかなように、メインモデルクラスGridModelは、プロパティとして存在する次のクラスで構成されています。

GridBodyModel:グリッド本体にレンダリングされる行のリストがあります。

GridContext:プロパティとして総ページ数があります。他のプロパティもありますが、それはこの説明の範囲外です。

GridHeaderModel:グリッドのヘッダーに表示する必要のあるセルのリストがあります。

次に、新しいページの読み込み時に実行されるこのスクリプトがあります。

$(document).ready(function () {
        // Apply Knockout view model bindings when document is in ready state.
        ko.applyBindings(Global_GridKOModel, document.getElementById("gridMainContainer"));
    });

// Serialize the server model object. It will be used to create observable model.
Global_GridKOModel = ko.mapping.fromJS (<%= DataFormatter.SerializeToJson(Model) %>);

Global_GridKOModelはグローバルJavaScript変数です。 モデルは、サーバーからのMVCグリッドモデルです。

ユーザーは、ページでさらに検索を再度実行できます。私はAjaxを介して新しい検索条件を投稿することでこれを処理します。この投稿では、新しいMVCモデルが作成され、Ajax応答として返送されます。次に、この新しいMVCモデルを使用して、ko.mappingを使用してGlobal_GridKOModelを更新します。これにより、新しいページの読み込み時に以前に構築されたグリッドが(新しいデータで)更新されます。これが私がやっている方法です。

$.ajax({
        url: destUrl,
        data: dataToSend
        success: function (result) {
            ko.mapping.fromJS(result, Global_GridKOModel);
        },
        error: function (request, textStatus, errorThrown) {
            alert(request.statusText);
        }
    });

次のシナリオを除いて、すべてが正常に機能しています。

結果が返されないAjaxリクエストが作成されます。つまり、GridBodyModelとGridHeaderModelがモデルGridModelでnullになります。そのタイムグリッドは、レコードが見つからなかったことを正しく示しています。正解です。これは、次のHTMLバインディングによって発生します。

<!-- When no record is found. -->
<div data-bind="ifnot: GridContext.GridPager.TotalPages() > 0">
    No record(s) were found.
</div>

<!-- This is actual grid table container. This is binded when records are found -->
<div data-bind="if: GridContext.TotalPages() > 0">

Grid construction happens here

</div>

この後、別のAjaxリクエストが行われたが、今回はレコードが返される場合(firebugで応答を確認し、レコードが実際に返されることを確認しました)。この時間グリッド構築は、さまざまな監視可能なアレイにアクセスする場合に発生します。たとえば、グリッドのページャーを作成するには、私が作成したHTMLバインディングを使用します。

<td data-bind="attr:{colspan: GridHeader.Cells().length }">

今回、KOはfirebugで見られる次のエラーをスローします。

バインディングを解析できません。メッセージ:TypeError:GridHeader.Cellsは関数ではありません。バインディング値:attr:{colspan:GridHeader.Cells()。length}

レコードが返される限りは正常に機能しますが、上記で説明したように、レコードが返されないと機能しなくなります。レコードが返されない場合、以前の応答ではGridHeaderがnullであったことに注意してください。ko.mappingで何か魚臭い匂いがします。監視可能な配列をマッピングする際に問題があると思います。

それで、私が正しくやっていないのは何ですか?誰かお願いします?

はっきりと述べていない場合は、遠慮なく説明を求めてください。

前もって感謝します。

4

2 に答える 2

2

動作の実際の理由は、元々GridHeaderがjavascriptオブジェクトであるためですが、GridHeaderプロパティに対してnullを使用してマッピングを返すと、null値で監視可能になり、今後のすべての更新で引き続き監視可能になります。

回避策は次のとおりです(1つ選択してください):

  1. 子モデルにnull値を返さないでください(代わりに空のオブジェクトを返します)-私はそれが好ましい方法だと思います
  2. または-マップを呼び出す前に、Global_GridKOModel.GridHeader=null;を実行します。(これにより、更新後にGridHeaderが監視可能にならないようになります)
  3. または-マッピング後、Global_GridKOModel.GridHeader = ko.unwrapObservable(Global_GridKOModel.GridHeader);を呼び出します。

あなたが持っている振る舞いの完全な説明は以下のコードにあります(jsfiddleにコピー-http ://jsfiddle.net/nickolsky/zrBuL/9/

console.log('step1');
var vm = ko.mapping.fromJS({ body: { children: [ 1, 2, 3]} });
console.log(vm.body.children()[1]); //prints 2

console.log('step2');
ko.mapping.fromJS({ body: { children: [ 4, 5, 6]} }, vm);
console.log(vm.body.children()[1]); //prints 5

console.log('step3');
//after next call, vm.body is no longer variable - it is now observable
ko.mapping.fromJS({ body: null }, vm);
console.log(vm.body); //prints observable
console.log(vm.body()); //prints null

console.log('step4');
//and it will remain observable for next call
ko.mapping.fromJS({ body: { children: [ 7, 8, 9]} }, vm);
console.log(vm.body().children()[1]); //prints 8
console.log(vm.body().children().length); //prints 3
console.log(vm.body); //prints observable function body
console.log(vm.body()); //prints object

//workaround for null issue - we can reset it to be null instead of null observable
//and it will get original behavior
vm.body = null;
console.log('step5');    
ko.mapping.fromJS({ body: { children: [ 7, 8, 9]} }, vm);
console.log(vm.body.children()[1]); //prints 8 - body is no longer observable again
​

私にとっては、マッピングプラグインの設計上の問題のように見えますが、子のviewmodelプロパティのマッピングをカスタマイズして、nullの場合に監視可能にならないようにする方法を見つけることができませんでした(「コピー」機能により、すべての内部コンテンツが観察不能、残念ながら)。

于 2012-05-18T14:28:44.340 に答える
1

私は昨夜それを理解しました。実際、これは私が犯した非常に基本的な間違いでした。nullに設定した後、配列にアクセスしようとしました。その方法を説明します。

その時点で結果が返されたとき、GridHeaderModelとその中のIListセルが定義されました。つまり、nullではありませんでした。その時、ko.mappingはモデルを変換し、モデルとその中にarraryを作成することができました。しかし、nooの結果が返され、GridHeaderModelがnullであるajaxリクエストが行われた場合、明らかにIListセルもnullでした。その時、ko.mappingは同じことを行い、それが更新したKOモデルは、GridHeaderModelもnullに設定し、内部の観測可能な配列Cellsは存在せず、nullと同じくらい良好です。いくつかのレコードが返された別のajaxリクエストを行ったとき、ko.mappingは、クライアントのKOモデルに存在しなかった(またはnullに設定された)監視可能な配列Cellsを更新しようとしましたが失敗しました。ajaxの代わりに新しいページの読み込みがあれば、すべてが機能していました。したがって、解決策は、KOモデルの更新のために、初期化されていない列挙型(監視可能な配列に変換される列挙型)をクライアントに返さないことです。したがって、レコードが返されない場合は、GridHeaderModelがnullでないことを確認し、要素が含まれていなくてもIListセルが初期化されていることを確認しました。これで問題が修正されました。

この問題は、次の例で説明できます。

public class Demo
{
    public IList<string> DemoArray;
}

public class DemoUser
{
    public void UseDemo()
        {
            var demoInstance = new Demo();
            demoInstance.DemoArray.Add("First Element");
        }
}

ここでは、UseDemo()メソッドでクラスインスタンスDemoを初期化しましたが、IListDemoArrayは初期化されていません。したがって、それにアクセスしようとすると、ランタイム例外がスローされます。これが私の場合に起こっていたことです。最初のajax応答では、監視可能な配列をnullに設定していました。つまり、初期化を解除してから、次のajax応答でアクセスしようとしました。

于 2012-05-19T06:15:10.853 に答える