Могу ли я программно добавлять теги к ‹head› из тела в Castle MonoRails / NVelocity?

Я пытался найти способ программно добавить ссылку на внешний файл CSS в тег <head> из разметки внутри тега <body> в Castle MonoRails и движке просмотра NVelocity. Кто-нибудь знает, как это можно сделать?

Мне нужно решить эту проблему, так как страница, над которой я работаю, может состоять из множества «виджетов», и каждый виджет будет оптимально получать дополнительные ресурсы, такие как JS и CSS, вместо того, чтобы помещать <link ..> в тег body и рисковать проблемами рендеринга.


person olemarius    schedule 31.05.2011    source источник


Ответы (4)


Представленные здесь решения не работают должным образом. Лучше всего добавить свои скрипты в httpContext.Current.Items.

Используя ту же структуру, что и @jishi:

в вашем представлении или в вашем представлении вы можете вызвать что-то вроде этого:

$Style.Add("/static/style1.css")

и в вашем макете (заголовок):

$Style.Render()

Ваш помощник состоит из двух простых методов, использующих httpcontext для хранения и получения списка файлов.

    public class StyleHelper
{
    public static void Add(string file) {
        if (string.IsNullOrWhiteSpace(file))
            return;

        var obj = HttpContext.Current.Items["AssetFiles"] as List<string>; // get the collection which might be empty
        if (obj == null || obj.Count == 0) {
            IList<string> list = new List<string>();
            list.Add(file.ToLower());
            HttpContext.Current.Items.Add("AssetFiles", list); // adds your first asset to your collection and returns
            return;
        }

        if (obj.Contains(file.ToLower()))
            return; // asset is already added

        // new asset ready to be added to your collection
        obj.Add(file.ToLower());
        HttpContext.Current.Items.Add("AssetFiles", obj);
    }

    public string Render() {
        var obj = HttpContext.Current.Items["AssetFiles"] as List<string>;
        if (obj == null || obj.Count == 0)
            return ""; // you never added anything to the collection. Nothing more to do.

        // not using linq here for the sake of readability:
        string res = string.Empty;
        foreach (var item in obj) {
            res = res + string.Format("<link rel=\"stylesheet\" {0} />", item);
        }
        return res;
    }

}

В вашем контроллере (желательно базовом контроллере) добавьте это:

[Helper (typeof (StyleHelper), «Style»)] открытый класс YourController

person Joakim    schedule 09.02.2013
comment
Хм, я не понимаю, зачем вам добавлять их в Context.Items. Помощник является временным для текущего запроса, поэтому внутреннее состояние достаточно хорошее. Я вижу, что он может сломаться, если вы добавите один и тот же помощник как к базовому контроллеру, так и к своему специализированному контроллеру. Итак, просто из любопытства, почему вы считаете, что лучше добавить его в Context.Items? Что мне не хватает? - person jishi; 11.02.2013
comment
старый вопрос :) Я действительно не помню, почему я добавил его в context.items, но, по крайней мере, у него есть одно огромное преимущество, в зависимости от дизайна остальной части приложения; если вы не знаете, вызывали ли вы метод ранее с тем же активом, этот код гарантирует, что вы не добавляете его несколько раз :) - person Joakim; 04.09.2020
comment
Чёрт возьми, спустя 7 лет я даже больше не использую NVelocity / Monorail :) - person jishi; 04.09.2020

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

Это означает, что в вашем представлении или в вашем представлении вы можете вызывать что-то вроде этого:

$Style.Add("/static/style1.css")

и в вашем макете (заголовок):

$Style.Render()

Вот пример помощника (наследование AbstractHelper необязательно):

public class StyleHelper : AbstractHelper
{
    private readonly HashedSet<string> sheets = new HashedSet<string>();

    public void Add( string styleSheet )
    {
        Add( styleSheet, "all" );
    }

    public void Add( string styleSheet, string media )
    {
        this.sheets.Add( String.Format( "media=\"{0}\" href=\"{1}\"", media, styleSheet ) );
    }

    public string Render()
    {
        var str = new StringBuilder();

        this.sheets.ToList().ForEach( sheet => str.AppendLine( String.Format( "<link rel=\"stylesheet\" {0} />", sheet ) ) );

        return str.ToString();
    }
}

AbstractHelper является частью Castle.Monorail.Framework.Helpers. Вам не нужно наследовать это, если вы не хотите использовать функциональность, предоставляемую этой абстрактной реализацией. Затем добавьте его к своему контроллеру, к вашему базовому контроллеру или конкретному контроллеру:

[Helper( typeof( StyleHelper ), "Style" )]
public class YourController
person jishi    schedule 31.05.2011
comment
Я дам вам знать, удастся ли нам это сделать :) В настоящее время нам нужно заставить его работать как без добавления ресурсов, так и с несколькими (узлами). Мы используем MonoRails 2.1, дайте мне знать, если вы знаете, что это возможно в этой версии. Спасибо! - person olemarius; 01.06.2011
comment
Добавлен простой пример для StyleHelper. Я надеюсь, что это помогает! - person jishi; 01.06.2011
comment
@jishi: Какова цель AbstractHelper в вашем примере кода? - person Joakim; 09.02.2013
comment
@Joakim: По-видимому, ничего, если подумать. Я извлек соответствующие части из нашего StyleHelper, который, вероятно, использовал некоторые функции, предоставляемые абстрактным классом. Думаю, я также предполагал, что мне нужно унаследовать какой-то интерфейс, чтобы зарегистрировать его в качестве помощника во время написания, что, как я знаю, сейчас не требуется. Спасибо что подметил это! - person jishi; 11.02.2013

CaptureFor

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

В представлении:

#capturefor(capturefortest)
    Default way of printing. Use this for unique variables, such as title.
#end

#blockcomponent(CaptureFor with "id=capturefortest" "append=before")
    This will append before 1.
#end

#blockcomponent(CaptureFor with "id=capturefortest" "append")
    This will append after 1.
#end

#blockcomponent(CaptureFor with "id=capturefortest" "append")
    This will append after 1 and 3.
#end 

#blockcomponent(CaptureFor with "id=capturefortest") 
    Overrides everything defined before this. 
#end

Макет / MasterPage:

$capturefortest

Экранирование #:

При добавлении #, например в селекторе идентификаторов jQuery внутри #blockcomponent (CaptureFor) вы получите сообщение об ошибке. Этого можно избежать, установив некоторые глобально доступные переменные в AssetFilter и используя $ {HASH} для печати #. Также неплохо иметь одинарные кавычки и кавычки:

controllerContext.PropertyBag.Add("HASH", "#");
controllerContext.PropertyBag.Add("Q", '"');
controllerContext.PropertyBag.Add("sq", "'");

Теперь смело можно делать:

#blockcomponent(CaptureFor with "id=capturefortest" "append=before")
    <script type="text/javascript">
        jQuery('#container').html('Awesomeness!')
    </script>
#end
person olemarius    schedule 05.06.2012

Это возможно благодаря Javascript! Вот как я бы добавил стиль к <head>:

<script type="text/javascript">
    var cssNode = document.createElement('link');
    cssNode.setAttribute('rel', 'stylesheet');
    cssNode.setAttribute('href', 'http://path.to/your/file.css');
    cssNode.setAttribute('type', 'text/css');
    document.getElementsByTagName('head')[0].appendChild(cssNode);
</script>
person jazgot    schedule 31.05.2011
comment
JavaScript действительно является одним из способов, но более оптимальным является выполнение его уже на стороне сервера. - person olemarius; 01.06.2011