WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Ajax Control Toolkit スクリプト バンドル

by WebSurfer 2018年6月10日 22:31

Ajax Control Toolkit は v7.x 以前では、使用する複数のスクリプトファイルをバンドルするために ToolkitScriptManager を使っていました。(ScriptManager ではなくて)

v15.1 以降では、DevExpress の記事 How to use Bundling and CDN にありますように、ScriptManager を使ってのバンドリングが可能となっています。(ちなみに、v15.1 以降 ToolkitScriptManager は提供されていません)

以下の画像の赤枠で囲った部分を見てください。これが Ajax Control Toolkit 用の複数のスクリプトファイルがバンドルされた結果です。

バンドルされたスクリプト

ASP.NET 4.5 以降で、Visual Studio Commnunity 2015 の[Web フォーム]のテンプレートを利用して作った ASP.NET Web Forms プロジェクトに、Ajax Control Toolkit を追加し、必要なスクリプトファイルを ScriptManager で統合できるようにしてみましたので、その方法や結果から分かったことを備忘録として書いておきます。

先の記事「ASP.NET 4.5 ScriptManage」に書きましたように、Microsoft Ajax、jQuery 等のスクリプトファイルが ScriptManager で統合できるようになり、[Web フォーム]のテンプレートを利用して作ったプロジェクトでは、それらが設定済みとなっています。

なので、そのプロジェクトに Ajax Control Toolkit 用のバンドル機能を追加すると、重複などの問題で修正が必要かも知れないと思っていましたが、自分が試した限りはそういうことはなかったです。

まず、NuGet で AjaxControlToolkit のパッケージをインストールします。それだけでも基本的なコントロールは動くようになりますが、ScriptManager を利用したバンドリングを可能にするためには、StaticResources パッケージも追加でインストールする必要があります。

NuGet パッケージの追加

バージョンは Ajax Control Toolkit 本体と合わせた方がよさそうです。ここでは、先にインストールした既存の Ajax Control Toolkit 本体が v17.1.1 なので、StaticResources のバージョンもそれに合わせて v17.1.1 にしました。

なお、Visual Studio で[空]のテンプレートを選択してプロジェクトを作った場合は StaticResources パッケージだけでは不足です。追加で Antlr, WebGrease などの NuGet パッケージもインストールする必要がありますが、StaticResources をインストールする際それらも自動的に追加されるはずです。

StaticResources のインストールが完了すると、以下の画像の赤枠で示したフォルダが生成され、その中に CSS ファイル、イメージ、スクリプトファイルが格納されます。

追加された CSS とスクリプト

Content/AjaxControlToolkit フォルダ下の Images フォルダには .gif, .jpg, .png などの画像ファイルが、Styles フォルダには minify してない .css ファイルと minify した .min.css ファイルの両方が格納されます。

Scripts/AjaxControlToolkit フォルダ下の Debug フォルダには minify してない .debug.js ファイルが、Release フォルダには minify した .js ファイルが格納されます。

さらに、web.config にはデフォルトで以下の設定が追加されます。

<ajaxControlToolkit 
    useStaticResources="true" 
    renderStyleLinks="false" />

DevExpress の記事 How to use Bundling and CDN の説明によるとバンドル機能をコントロールするもののようです。

あとは、DevExpress の記事 "Manual changes required" のセクションを参考に、ScriptManager に ScriptReference を追加してやればスクリプトは一番上の画像に示したようにバンドルされます。

具体的には以下の画像の赤枠で示したコードを追加してやります。

ScriptReference の追加

赤枠部分以外の ScriptManager のコードは、Visual Studio の[Web フォーム]のテンプレートを利用して作った ASP.NET Web Forms アプリのマスタページ Site.master に自動生成されたものです。

.css ファイルについても、DevExpress の記事に従って Styles.Render をページの適当な場所に追加してやれば、.css ファイルを参照する link 要素がレンダリングされます。

ただし、.css ファイルは web.config の compilation 要素で debug="false" とするか、Global.asax の Application_Start メソッドで BundleTable.EnableOptimizations を true に設定しないとバンドルされませんので注意してください。(スクリプトファイルと異なります。理由不明)

BundleTable.EnableOptimizations を true に設定する意味については Microsoft の文書 Bundling and Minification を見てください。抜粋すると "The following code (true に設定すること) enables bundling and minification and overrides any setting in the Web.config file." ということだそうです。

web.config の compilation 要素の debug 属性の true / false でどう異なるかですが、自分が検証した限りでは、

  • .css ファイル: true で minify &バンドルあり、false で minify なしでバンドルもなし。
  • スクリプトファイル: true / false の設定にかかわらず minify &バンドルあり。

・・・となりました。

スクリプトファイルの結果がちょっと解せないです。そのままでは、debug="true" にしてもスクリプトのデバッグができません。

.debug.js 版をダウンロードさせてデバッグ可能なようにするには、上の ScriptManager の設定のところで ScriptReference をコメントアウトする他なさそうです。

あと、minify&バンドルされたスクリプトファイルのサイズですが、何と 1,248,841 バイトもありました。Ajax Control Toolkit は使わないのにスクリプトをダウンロードするように設定するというのは避けた方がよさそうです。

ちなみに、.CSS ファイルの方は minify&バンドル後で 56,240 バイトでした。

最後にもう一つ、HtmlEditorEntender には AjaxControlToolkit.HtmlEditor.Sanitizer, HtmlAgilityPack が必要です。

それなしでは先の記事「Ajax Control Toolkit デモ」で述べたような意味不明のエラーが出てハマるかもしれませんのでご注意ください。

Tags: , ,

AJAX

Dictionary の JSON シリアライズ

by WebSurfer 2018年6月2日 18:56

Dictionary<TKey, TValue> クラスのオブジェクトを、ASP.NET MVC の Controller クラスの Json メソッドなどで使われている JavaScriptSerializer クラスを用いて JSON 文字列にシリアライズするとどうなるかという話を書きます。

Dictionary の JSON シリアライズ

例えば、以下のような Controller のアクションメソッドで Dictionary<string, Car> オブジェクトを作って Json メソッドで JSON 文字列にシリアライズしてみます。(注: TKey は JSON の「名前:値」ペアの「名前」になるので string 型にする必要があるようです。実際 int 型ではエラーになりました)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Mvc5App.Controllers
{
  public class Car
  {
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public float Price { get; set; }
  }

  public class HomeController : Controller
  {        
    public ActionResult JsonDictionary()
    {
      Dictionary<string, Car> dic = 
                          new Dictionary<string, Car>()
      {
        { "Car1", new Car() { Make="Audi",
            Model="A4", Year=1995, Price=2995f } },
        { "Car2", new Car() { Make="Ford",
            Model="Focus", Year=2002, Price=3250f } },
        { "Car3", new Car()  {Make="BMW",
            Model ="503i",Year=1997,Price=1995f } }
      };            

      return Json(dic);
    }
  }
}

上のアクションメソッドをブラウザから POST 要求すると、以下の JSON 文字列が応答として返ってきます。(注: POST 要求するのは、Json メソッドはデフォルトでは GET 要求を拒否するからです)

{
"Car1":{"Make":"Audi","Model":"A4","Year":1995,"Price":2995},
"Car2":{"Make":"Ford","Model":"Focus","Year":2002,"Price":3250},
"Car3":{"Make":"BMW","Model":"503i","Year":1997,"Price":1995}
}

つまり、JSON の「名前:値」ペアとして、Dictionary<TKey, TValue> の TKey が「名前」に、 TValue(Car の JavaScript オブジェクトの JSON 文字列) が「値」に設定され、そのペアが Dictionary の要素の数(上の例では 3 つ)並べられた JSON 文字列になります。

知ってました? 実は自分は知らなかったです。(汗) Dictionary<TKey, TValue> なんて JSON にシリアライズできないと思ってました。(恥)

デシリアライズも、もちろんできるようです。例えば、jQuery alax を利用して上記アクションメソッドを呼び出すと、自動的に JSON 文字列が JavaScript オブジェクトにデシリアライズされ、コールバックの引数として渡されます。

以下の例を見てください、function(data) ... の引数 data にデシリアライズされた JavaScript オブジェクトが渡されます。それを jsonresult という id を持つ div 要素の中に書き込んだのが上の画像です。

function jsonDictionary() {
    $.ajax({
        type: 'POST',
        url: '/home/jsonDictionary'
    })
    .done(function (data) {
        $("#jsonresult").empty();
        $.each(data, function (index, car) {
            $('#jsonresult').append(
                '<p><strong>' + index + '</strong></p>' +
                '<p>' + car.Make + ' ' + car.Model +
                ', Year: ' + car.Year +
                ', Price: $' + car.Price + '</p>');
        });
    })
    .fail(function (jqXHR, textStatus, errorThrown) {
        $("#jsonresult").text('textStatus: ' + textStatus +
          ', errorThrown: ' + errorThrown);
    })
}

上のコードの例では、jQuery の jQuery.each() メソッドを使っていますが、JavaScript の for ... in ループを使って同じ処置を行う例も書いておきます。

for (var key in data) {
    $('#jsonresult').append(
        '<p><strong>' + key + '</strong></p>' +
        '<p>' + data[key].Make + ' ' + data[key].Model +
        ', Year: ' + data[key].Year +
        ', Price: $' + data[key].Price + '</p>');
}

元の .NET Framework の Dictionary<string, Car> オブジェクトにも JavaScriptSerializer や Json.NET (Newtonsoft.Json) を使ってデシリアライズできます。

string json = "上の JSON 文字列";

// JavaScriptSerializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, Car> dic =
    serializer.Deserialize<Dictionary<string, Car>>(json);

// Json.NET (Newtonsoft.Json)
Dictionary<string, Car> dic =
  JsonConvert.DeserializeObject<Dictionary<string, Car>>(json);

ただし、DataContractJsonSerializer では、自分が試した限りですが、ダメでした。何かやり方はあるのかもしれませんが。

Tags: ,

JavaScript

MVC5 で AjaxHelper が働かない

by WebSurfer 2018年5月28日 15:40

Visual Studio Community 2015(以下、VS2015 と書きます)のテンプレートで [MVC] を選択して生成した ASP.NET MVC5 アプリケーションでは、そのままでは AjaxHelper(Ajax.BeginForm, Ajax.ActionLink など)が働きません。その理由と解決策を書きます。

Ajax.ActionLink を利用したページ

上の画像は Ajax.ActionLink を使って a 要素の href 属性に部分ビューの url を設定したハイパーリンクをレンダリングし(青文字の部分)、ユーザーがハイパーリンクをクリックすると Ajax を利用して部分ビューを呼び出し、応答を UpdateTargetId に指定した div 要素内に書き出した結果です(Customer Details 以下の部分)。

上の画像のアプリは対策済みなので、Ajax を利用した部分ビューの呼び出しと、UpdateTargetId に指定した div 要素内へ応答の書き出しは成功しています。

未対策の場合はハイパーリンクは普通の a 要素の機能しかないので、クリックすると部分ビューは同期呼び出しされ、ブラウザの現在のウィンドウ全体が部分ビューに書き換えられて表示されます。

なぜそうなるかと言うと、必要な外部 .js ファイルがダウンロードされる設定がされてないからです。

そんな設定は自分でやるのが当たり前と言われるかもしれませんが、Visual Studio 2010 Professional(以下、VS2010 と書きます)のテンプレートで自動生成される MVC4 アプリのプロジェクトでは、AjaxHelper に必要な外部 .js ファイルがダウンロードされる設定が含まれているのです。

なので、VS2015 で MVC5 アプリのプロジェクトを作って、既存の MVC4 の AjaxHelper を使うコードを MVC5 に移植しただけでは期待した Ajax の機能は働きません。

何を隠そう、自分はその理由が分からずハマってしまいました。(汗) また無駄な時間を費やすことがないよう、以下に備忘録として原因と対策を書いておきます。

MVC4 以降は控えめな JavaScript を利用して、Ajax.BeginForm, Ajax.ActionLink などから生成される form 要素、a 要素の属性の設定に応じて Ajax 呼び出しと応答の表示を行うようになっています。

その控えめな JavaScript は jquery.unobtrusive-ajax.js または jquery.unobtrusive-ajax.min.js というファイルに含まれています。(ちなみに、MVC3 以前は「控えめ」ではない MicrosoftAjax.js と MicrosoftMvcAjax.js を使います)

例えば、Ajax.ActionLink を使った場合、上の画像の例の「Ms. Rosmarie Carroll」のハイパーリンクの html 要素は以下のようになります。

<a  data-ajax="true" 
    data-ajax-mode="replace" 
    data-ajax-update="#results" 
    href="/Customer/Details/6">
    Ms. Rosmarie Carroll
</a>

控えめな JavaScript は、上の a 要素の属性 data-ajax, data-ajax-mode, data-ajax-update の設定に従って Ajax 呼び出しと応答の処理を行います。

従って、やるべきことは控えめな JavaScript の外部ファイルがダウンロードされるように設定を行うことです。VS2010 で作る MVC4 アプリはそのあたりの設定が自動的に行われますが、VS2015 で作る MVC5 アプリは開発者が自分で設定しなければなりません。

前置きが長くなりましたが、その方法を以下に書きます。

まず、NuGet から Microsoft.jQuery.Unobstrusive.Ajax をインストールします。以下の画像を見てください。

NuGet からインストール

インストールが完了すると、Script フォルダに jquery.unobtrusive-ajax.js, jquery.unobtrusive-ajax.min.js がインストールされるはずですのでチェックしてください。

スクリプトファイル

App_Start フォルダの BundleConfig.cs ファイルを開いて上記 .js ファイルがバンドルされるように設定します。下の画像の赤枠部分を見てください。ここでは MVC4 と同様に、クライアント側での検証用のスクリプトファイルと一緒にダウンロードされるようにしています。

BundleConfig.cs の設定

_Layout.cshtml には @RenderSection("scripts", required: false) が含まれているので、_Layout.cshtml を使う View では以下のコードを追加すれば OK です。

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

_Layout.cshtml を使わない View の場合は @Scripts.Render("~/bundles/jqueryval") の位置が jQuery.js の後に来るように注意してください。

Tags:

MVC

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年10月  >>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar