トップ回答者
[ASP.NET MVC3]JsonResultを指定したビューに返したい

質問
-
環境環境:
Windows Vista SP1
VisualStudio 2010 Sp1 Pro
ASP.NET MVC3 + jquery mobile
IIS7http://localhost:54371/Viewer/Job/Todayで今日の仕事の一覧を表示するようなアプリケーションがあります。
う~ん、わかりづらい・・・。
このときJobコントローラのTodayアクションメソッドを呼び出しています。
ですが、Todayというビューは存在せず、JobListというビューが存在し、
Todayアクションのリターン文はreturn View("JobList", model);となっています。
JobListは次のように記述され、仕事をリスト表示しています。
(ビューへ型付けされたモデルを渡しています)
<ul data-role="listview" data-theme="c" data-dividertheme="b" id="joblist">
@foreach (var item in Model) {
<li>
<h3>item.JOB_NAME</h3>
<p>@String.Format("{0:t}", item.DELIVERY_DATE)</p>
<span class="ui-li-count">@item.DELIVERY_COUNT</span>
</li>
}
</ul>
Todayの結果を描画するビューが違うのはJobListを別のアクションメソッドでも利用しているからす。
(TomorrowとかYesterdayとか)
アクションメソッド名と描画するビューが違うスタイルを維持したまま、
HTML描画部分をModel+Razorから、JavaScriptに置き換えたいと考えています。
(ヘルパーメソッドを自作してがんばればできるかもしれませんが、JavaScriptの方が書きやすい部分がありまして。
あと後学のために。)
描画部分のjavaScriptはできています。
あとは、そいつの呼び出しタイミングとそいつへのデータの渡し方なんですが、
私が考えたのは
Ajax.ActionLinkの引数でAjaxOpsionsにデータ取得成功時にjavaScriptを指定し、そこでDOMを操作するやり方です。
ただ、Ajax.ActionLinkはなんか違う気がします。あとjquery mobileとの相性が悪い気がします。
なので理想ははHtml.ActionLinkにAjaxOpsionsが指定できればいいんですけどね。
何か良い方法はないものでしょうか?
回答
すべての返信
-
>K.Takaokaさん
rel="external"で無効化できるとは知りませんでした。
ありがとうございます。
jquery mobile+Ajax.ActionLinkでリンクすると、
2度アクションメソッドが呼び出されることがあるなど、おかしな状況だったのが改善しました。
OnSuccessならデータも受け取れますし利用できそうです。
ただ、jquery mobileのdata-transition機能(ページ移動時にかっこいいアクションが出る)も無効化されてしまって
少し悲しいですが、しゃーないですねpq
あと、タイトルとは関係ない疑問なんですが、OnCompleteはOnSuccessの前に呼び出されるはずなのに。
alert()を差し込んで調べてみるとどう見てもOnSuccessが先に起動するんです。なぜでしょうね? -
> data-transition機能(ページ移動時にかっこいいアクションが出る)も無効化されてしまって
ここまでの話で、そのようなことにはならないはずです。なるとしたら、OnComplete や OnSuccess にご自身で書かれたスクリプトが、そのようになっているだけです。http://jquerymobile.com/test/docs/pages/docs-navmodel.html#../../docs/api/methods.html あたりをきちんと読んでみてください。
function request_success() { var newPage = $('<div />', { id: 'myNewPageFromAjax', data-role: 'page', }).appendTo(document.body); $(<div />', { data-role: 'content', html: 'test page from Ajax.ActionLink' }).appendTo(newPage); $.mobile.changePage( 'myNewPageFromAjax', 'slideup); }
みたいなかんじですかね?
-
一応、書いておきますが、Ajax でのリクエストを含めてこのような
- バックグラウンドでの Ajax 等を利用した HTML の読み込み
- それを現在ページに DIV タグで埋め込み
- 埋め込んだ DIV タグへのアニメーションでの画面遷移
- 画面遷移の現在位置管理と、相対パスのサポート
を、全自動でやってくれるのが jQuery mobile です。ですので、やろうとされていることは jQuery mobile を使用しないで jQuery mobile と同じようなことをしようとしているということです。それは理解されていますね?
jQuery mobile で上記の操作を自動化するために、ASP.NET MVC では Html.ActionLink() を使用します。そして、そのリクエスト先のアクションで <div data-role=page> を含んだ webpage を返します。それだけで必要な処理はすべて jQuery mobile がやってくれます。
-
>K.Takaokaさん
>を、全自動でやってくれるのが jQuery mobile です。ですので、やろうとされていることは jQuery mobile を使用しないで jQuery mobile と同じようなことをしようとしているということです。それは理解されていますね?
申し訳ないです。理解していなかったと認めざる得ません。
しかし今回の質問を通じてjquery mobileの仕組みについて理解が深まりました。感謝しています。
ただ、少し応用になると唸ってしまうのでまだまだ理解が足りていないのも事実です。それを踏まえた上で以下の点に疑問があります。
> data-transition機能(ページ移動時にかっこいいアクションが出る)も無効化されてしまって
に見えましたが、実は別の現象だったようです。
Ajax.ActionLink("今日の仕事", "Today", "Job", null, new AjaxOptions { OnComplete = "request_success"}, new { rel = "external" })
から
<a data-ajax="true" data-ajax-complete="request_success" href="/Viewer/Job/Today" rel="external">今日の仕事</a>
ようなHTMLが生成されています。
このリンクをクリックすると、ajax呼び出しが行われず、外部リンクのように扱われてしまいます。
URLがhttp://localhost:54371/Viewer/Job/Todayに切り替わりますし、TodayアクションメソッドでRequest.IsAjaxRequest()を調べてみるとfalseです。
href属性だけ見るとページ内リンクには見えないので当然かなとも思いますし、そこはASP.NET MVCがなんとかしてくれないのかとも思いますし。
このような呼び出しになる理由がわかりません。
(ASP.NET MVCのルーティングやアクションメソッドと対応するビューが異なることが影響しているのか・・・)
また、これはajax呼び出しになるのが正しいと考えると、K.Takaokaさんに示して頂いたjQuery mobilの機能(1~4)が働き、$.mobile.changePage()はいらないと思うのですがどうなんでしょうか。
-
それを踏まえた上で以下の点に疑問があります。
> data-transition機能(ページ移動時にかっこいいアクションが出る)も無効化されてしまって
に見えましたが、実は別の現象だったようです。
Ajax.ActionLink("今日の仕事", "Today", "Job", null, new AjaxOptions { OnComplete = "request_success"}, new { rel = "external" })
から
<a data-ajax="true" data-ajax-complete="request_success" href="/Viewer/Job/Today" rel="external">今日の仕事</a>
ようなHTMLが生成されています。
このリンクをクリックすると、ajax呼び出しが行われず、外部リンクのように扱われてしまいます。
URLがhttp://localhost:54371/Viewer/Job/Todayに切り替わりますし、TodayアクションメソッドでRequest.IsAjaxRequest()を調べてみるとfalseです。
href属性だけ見るとページ内リンクには見えないので当然かなとも思いますし、そこはASP.NET MVCがなんとかしてくれないのかとも思いますし。
このような呼び出しになる理由がわかりません。
生成される HTML タグは正しいと思います。jquery.unobtrusive-ajax.min.js をインクルードしていない、なんてことはありませんか?
参考:
Unobtrusive JavaScript in ASP.NET MVC 3 AjaxHelpers
http://davidhayden.com/blog/dave/archive/2010/12/30/UnobtrusiveJavaScriptASPNETMVC3AjaxHelpers.aspx
この記事の中では Action が JavaScript そのものを返していますが、この部分を Json を返すようにすればいいと思います。
軽く試してみましたが、アンカー タグのリンク先にページが遷移してしまうことはありませんでした。 -
>totojoさん
jquery.unobtrusive-ajax.min.jsはインクルードしています。
よくわかっていませんが、jquery mobileと相性が悪そうです。
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.mobile-1.0a3.min.js")" type="text/javascript"></script>
現在はこのように読み込んでいるんですが、jquery mobileを削除するとうまく動きました。
また順番がおかしいのかとも思い、unobtrusive-ajaxとjquery mobileの読み込み順番を入れ替えてみたところ、
アクションメソッドが、Ajaxと通常呼び出しの2度行われる現象が確認できました。
ふむむ。
-
いっそのこと Ajax.ActionLink を使わずに、Html.ActionLink にしてしまうというのはいかがでしょうか?
リンクのクリック時に自力で Ajax 呼び出しをしますので、若干コード量は増えてしまいますが。
Html.ActionLink ヘルパーを使います。ここで HTML 属性に id も仕込んでおきます。
@Ajax.ActionLink("今日の仕事", "Today", "Job", null, new { id = "myLink", rel = "external" })
このままではリンク クリック時に間違いなくページが遷移してしまうため、JavaScript でリンク クリック時に自力で Ajax 呼び出しをします。
$(function () { $('#myLink').click(function () { $.post(this.href, null, function (data) { // ここで Json データを処理する。 }, "json"); return false; }); });
return false でクリック自体はなかったことにしてあげるのがミソです。 -
> このリンクをクリックすると、ajax呼び出しが行われず、外部リンクのように扱われてしまいます。
あー、そうですね。jQuery mobile が external を処理してしまうので jQuery の ajax 呼び出しが呼ばれないことになっちゃってますね。すいません。> これはajax呼び出しになるのが正しいと考えると
external リンクの場合、jQuery mobile も jQuery も何もしませんので、通常の Ajax 呼び出しにすらならないです。Html.ActionLink() でも Ajax.ActionLink() でも良いのですが、external かつ javascript: なリンクを生成する必要があります。Ajax.ActionLink() を使用して jQuery のメソッドを呼ぶのが簡単かもしれません。
> $.mobile.changePage()はいらないと思うのですがどうなんでしょうか
jQuery mobile では、ページ遷移はすべて changePage() で行います。これを経由しないでページ(ここでいうページは、jQuery mobile が管理している data-role=page のことです)を切り替えると、base の値や現在の相対 URL を見失ってしまってその後のリンクが挙動不審になります。
-
> Ajaxでデータ取得してHTML書き換えて、$.mobile.chagePage()することがとても現実的で素直に思えてきました。
ちがうと思います。前にも書いていますが、jQuery mobiel では、何も考えずに HTML で ページ(data-role=page) を返すURLに対して Html.ActionLink("action", "controller") を使用することが想定された使い方です。そうやて作成された <a href="controller/action"> というリンクをたどると、jQuery mobile はそのクリック操作を Ajax を使用したバックグラウンドの読み込みと div タグの生成、生成されたページへのトランジションに書き換えてくれます。そして、その際に発生した URL の差異を、読み込んだページのすべてのリンクに反映してくれます。
こういった標準の挙動を無視して、動的に HTML を作成するなら、もうすこし jQuery mobile のドキュメントは読み込んだほうがいいと思います。(上の動作の記述も、かなりざっくりとおおまかにかいてますよ)
-
>K.Takaokaさん
>external リンクの場合、jQuery mobile も jQuery も何もしませんので、通常の Ajax 呼び出しにすらならないです。
試しに一つのHTMLに二つのページを設け、ページ移動のリンクにexternal属性を付与してみたところ、jQuery mobileのchangePage()が呼ばれているようなアクションが行われました。
公式の仕様がこうだとは示せないのですが、何が条件があるように思えます。
>> Ajaxでデータ取得してHTML書き換えて、$.mobile.chagePage()することがとても現実的で素直に思えてきました。
>ちがうと思います。
違うというのが、やり方ではなく、「現実的で素直」と捉えている考え方に対して言っていると解釈します。
確かに、jQuery mobileを使うサイトを構築しようとしているのに、あえて挙動を無視するやり方を推し進めるのもどうかと思いますが、
臨機応変に対応するという点に関しては目的を達成する上で必要なことだとも感じています。
その際、マニュアルの熟読が必要なことは同意します。
-
> 試しに一つのHTMLに二つのページを設け、ページ移動のリンクにexternal属性を付与してみたところ、jQuery mobileのchangePage()が呼ばれているようなアクションが行われました。
私の認識違いがあるかもしれません。帰宅したら確認してみます。
> jQuery mobileを使うサイトを構築しようとしているのに、あえて挙動を無視するやり方を推し進めるのもどうかと思いますが、
挙動どうこう以前の問題で、jQuery mobile の読み込みをやめたほうがよいのではないか?と思います。jQuery の機能だけしか使わないなら jQuery だけでよいのでは? jQuery mobile はロードしただけで様々な制約が発生する上に、まだアルファ版なので大掛かりな仕様変更等が発生しうるライブラリです。jQuery mobile を使用しないのに読み込んでおいて、jQuery mobileの制約に苦しめられているのでは、損しかしていないのではないかと思います。
-
私の認識通り、external がついていればきちんと外部ページとして通常のブラウザ側のページ遷移が発生しました。
<html> <div data-role="page" id="page1"> <div data-role="header">Page.1</div> <div data-role="contents"> <a href="page2">jump to page2 by jQuery mobile</a><br/> <a href="page2" rel="external">jump to page2 by web browser</a><br/> </div> <div data-role="page" id="page2"> <div data-role="header">Page.2</div> <div data-role="contents"> welcome to page2. </div> </div> </html>
-
>K.Takaokaさん
内部リンクってアンカーつけるものと思っていました。
アンカーなしでもページリンクになるんですね。確かに、アンカー無しだとexternalでjquery mobileが無効になります。
私はアンカーつけて確認していたんですが、これだとやはりexternalが効いていない感じでした。
http://jquerymobile.com/demos/1.0a4.1/docs/pages/docs-pages.html
を読むと内部リンクはアンカーをつけると解釈できいるのですがどうなんでしょうか。×あと関係ないですが、上記HTML1ページのコンテンツ内に2ページが入ってておかしくないですか?
↓私が確認したHTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> <script src="http://code.jquery.com/jquery-1.5.2.min.js"></script> <script src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> </head> <body> <div data-role="page" id="page1"> <div data-role="header"> <h1>Page.1</h1> </div><!-- /header --> <div data-role="contents"> <a href="#page2">jump to page2 by jQuery mobile</a><br/> <a href="#page2" rel="external">jump to page2 by web browser</a><br/> </div><!-- /content --> </div><!-- /page --> <div data-role="page" id="page2"> <div data-role="header"> <h1>Page.2</h1> </div><!-- /header --> <div data-role="contents"> welcome to page2. </div><!-- /content --> </div><!-- /page --> </body> </html>
- 編集済み NZ-000 2011年4月18日 1:27 あぁ、page1を閉じるdivタグがないようです。
-
仕事場では動作確認まではやれないので、ざっくりとした話しかできませんが、
jQuery mobile では、 external リンクに # で開始するものを指定することが想定されていないというのがシンプルな結論だと思います。
まず、ローカルリンクの記述方法ですが、href="page2" でも href="#page2" でも動作します。ただし、jQuery mobile では、jQuery が有効でないブラウザにおける挙動や jQuery mobile を使用しない場合の記述方法に差異がないことを目標の1つとしているので #page2 と記述するべきでしょう。
external ではないリンクの場合、href に指定された URL の解釈は jQuery mobile が行います。このため、href= に記載する URL は jQuery mobile の現在位置からの相対パスとして解釈されます。(jQuery mobile の Base タグ対応の問題点についてのドキュメントにあったと思いますが、リンク先を jQuery mobile の相対パスに変換します。) これに対して、external が指定されたリンクの href に指定された URL の解釈は、通常通りにブラウザが行います。このため、ブラウザからみた相対パスとして解釈されます。
ここからは想像ですが、このような挙動のため href="#page2" rel="external" という jQuery mobile がサポートしない形式のリンクを使用した際の挙動は保証されていないと思います。この場合、ブラウザは jQuery mobile の位置情報を完全に無視して、page2 という ID をもつ場所へ飛びます。結果として、ブラウザの認識している URL と、jQuery mobile の位置情報がたまたま一致しているときのみ、期待した場所に移動できることになります。(jQuery mobile を使用していて、そのような状態になることはトップページ等の非常に限定されたページだけだろうと思います)
確認していませんが、トランジション エフェクトについても、jQuery mobile がブラウザ側の機能によってナビゲートされたことを認識して行っているのではないかと思いますので、属性等は無視されているのではないでしょうか?と予想します。