Как перехватить визуализацию представления, чтобы добавить HTML/JS во все частичные представления?

Мне нужно написать содержимое js файла из управляемой конвенцией местоположения (например, ~/ClientApp/Controllers/Home/Home.js, если вы загружаете представление, расположенное в ~/Views/Home/Home.cshtml). Как это сделать?

Пример: если файл ~/Views/Home/Home.cshtml выглядит так:

<div id="some-partial-view">
   <!-- ... -->
</div>

И файл ~/ClientApp/Controllers/Home/Home.Controller.js выглядит как

function HomeController() {
  //some code
}

Затем отображаемый вид, возвращаемый веб-сервером, должен выглядеть (если используется скрипт)

<!--ommitted <html> <body> tags -->

<div id="some-partial-view">
   <!-- ... -->
</div>

<script type="text/javascript">
   function HomeController() {
       //some code
   }
</script>

Один из способов - добавить помощника HTML, который будет делать это, например:

<div id="some-partial-view" ng:Controller="HomeController">
   <!-- ... -->
</div>
@Html.IncludeController("HomeController") 

Однако я не хочу повторять это во всех частичных представлениях.

Любые идеи?

Ответ 1

Вы можете написать пользовательский вид:

public class MyRazorView : RazorView
{
    public MyRazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable<string> viewStartFileExtensions, IViewPageActivator viewPageActivator)
        : base(controllerContext, viewPath, layoutPath, runViewStartPages, viewStartFileExtensions, viewPageActivator)
    {

    }

    protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance)
    {
        base.RenderView(viewContext, writer, instance);

        var view = (BuildManagerCompiledView)viewContext.View;
        var context = viewContext.HttpContext;
        var path = context.Server.MapPath(view.ViewPath);
        var viewName = Path.GetFileNameWithoutExtension(path);
        var controller = viewContext.RouteData.GetRequiredString("controller");
        var js = context.Server.MapPath(
            string.Format(
                "~/ClientApp/Controllers/{0}/{0}.{1}.js",
                viewName,
                controller
            )
        );
        if (File.Exists(js))
        {
            writer.WriteLine(
                string.Format(
                    "<script type=\"text/javascript\">{0}</script>",
                    File.ReadAllText(js)
                )
            );
        }
    }
}

и настраиваемый механизм просмотра, который будет возвращать этот пользовательский вид при запросе частичного представления:

public class MyRazorViewEngine : RazorViewEngine
{
    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        return new MyRazorView(
            controllerContext, 
            partialPath, 
            null, 
            false, 
            base.FileExtensions, 
            base.ViewPageActivator
        );
    }
}

который будет зарегистрирован в Application_Start:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new MyRazorViewEngine());
}

Возможно, вам, возможно, придется отрегулировать некоторые из путей, поскольку в вашем вопросе было не совсем ясно, где именно должно быть расположено js, но обычно у вас должно быть достаточно деталей в ответе.

Ответ 2

было бы разумнее включить все javascript в рингтон, а затем включить это через index.php или где угодно...

Лично я использую jQuery, и я включаю каждый объект так...

//View Objects
(function($){

var views = {

   init: function()
   {
      this.view1();
      this.view2();
   },
   view1 : function()
   {
     // I am
   },
   view2 : function()
   {
     // all yours
   }

}
//call/invoke my view objects
$(function(){

    //call view object via init()
    view.init();
});

})(jQuery);