この種の質問はよく聞かれますが、問題をもう少し興味深いものにする、より具体的な制約があると思います。MVC パターンを使用して Dart でクライアント側アプリケーションを作成しています。私の目標は単純です。ボタンのクリックをリッスンし、バックエンド API への非同期要求をトリガーし、そのデータをユーザーに提示します。
少なくとも、モデル、ビュー、およびコントローラー クラスをそれぞれ 1 つずつ持っています。モデル クラスは、リクエストを作成し、受信したデータをまとめるメソッドを実装します。ビュー クラスは、関心のある DOM サブツリーをフィールドとして持ち、その中の要素を操作するメソッドを実装します。コントローラーは、モデル クラスとビュー クラスのそれぞれのインスタンスを 1 つフィールドとして持ち、ビューの要素にイベント ハンドラーを登録します。コントローラーのイベント ハンドラーは、モデルへの呼び出しを開始して要求を行い、データを返します。データはレンダリングのためにビューに渡されます。
非同期リクエストからモデルのインスタンス変数に受信データをキャプチャしようとすると、問題が発生します。すべてを適切にカプセル化しておきたい (そのため、最初に Dart を使用しています)。また、非同期要求からのデータを保持するためにグローバル変数を使用することは避けたいと考えています。私の現在のレイアウトの最小限の例は、以下のようになります。わかりやすくするために、ここではすべてのフィールドとメソッドを公開しました。
// view.dart
class FooView {
// The root element of the view with which we're concerned.
static final Element root = querySelector('#thisView');
FooView() { init(); }
void init() { root.hidden = false; }
// Appends the new data into an unordered list.
void update(List<Map<String,String>> list) {
UListElement container = root.querySelector('ul#dataContainer');
container
..hidden = true
..children.clear();
for ( Map<String,String> item in list ) {
container.append(new LIElement()
..id = item['id']
..text = item['text']
);
container.hidden = false;
}
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
List<Map<String,String>> dataList;
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
HttpRequest
.getString('example.com/api/endpoint')
.then( (String data) {
dataList = JSON.decode(data);
});
return dataList;
}
}
// controller.dart
class FooController {
FooModel model;
FooView view;
FooController() {
model = new FooModel;
view = new FooView;
}
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
view.update(model.getData());
});
}
}
私が見ているエラーは、このすべての最後に現れるmodel.dataList
フィールドに関係しています。null
私の最初の赤面は、コールバック関数のスコープを理解していないことです。私が最初に理解した方法では、コールバックはリクエストのデータが到着したときにそれを処理し、準備ができたらインスタンス変数を設定するだけでした。おそらく、インスタンス変数はエイリアス化され、コールバックのスコープ内で変更されますが、戻りたい変数は決して変更されません。
オブジェクトをビューのメソッドに渡すことを考えましFuture
た。これにより、処理自体が行われ、要素が副作用として DOM に追加されます。その手法は、私の MVC 設計を壊してしまいます (この最小限の作業例で今壊れている以上に)。
また、非同期プログラミングを完全に間違って使用している可能性も非常に高いです。view.update()
これについてもっと考えると、イベントが発生したときに基本的にコントローラーでブロッキング呼び出しを行うため、非同期呼び出しは役に立ちません。Future
リクエストをコントローラーに渡しthen()
、イベント ハンドラーがトリガーされたときにそこからリクエストのメソッドを起動する必要があるかもしれません。
Dart では、コールバック関数はどのスコープに存在し、最小限の副作用と最大限のカプセル化でそれらからデータを取得するにはどうすればよいですか?
注意: このよく議論される質問について詳しく説明するのは嫌いですが、同様の質問に対する以前の回答を読みましたが、役に立ちませんでした。