a ~ li {
background: red
}
В css3 добавлен новый селектор, поэтому, если html выглядит так:
<ul>
<li>
<a>Some text</a>
</li>
</ul>
Фактически вы можете воздействовать на родителя элемента «a», который является «li» «
Из решений в этой нити я придумал следующее, вероятно, сверхсложное решение, которое позволяет вам отложить рендеринг любых html (скриптов) в блоке использования.
@using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
<script>
someInlineScript();
</script>
}
@using (Html.Delayed()) {
<b>show me multiple times, @Model.Whatever</b>
}
when-i-call-you
: @using (Html.Delayed("when-i-call-you", isOnlyOne: "different unique name")) {
<b>show me once by name</b>
<span>@Model.First().Value</span>
}
(т.е. отобразить задержанную секцию в родительском представлении)
@Html.RenderDelayed(); // writes unnamed sections (#1 and #2, excluding #3)
@Html.RenderDelayed("when-i-call-you", false); // writes the specified block, and ignore the `isOnlyOne` setting so we can dump it again
@Html.RenderDelayed("when-i-call-you"); // render the specified block by name
@Html.RenderDelayed("when-i-call-you"); // since it was "popped" in the last call, won't render anything due to `isOnlyOne` provided in `Html.Delayed`
public static class HtmlRenderExtensions {
/// <summary>
/// Delegate script/resource/etc injection until the end of the page
/// <para>@via https://stackoverflow.com/a/14127332/1037948 and http://jadnb.wordpress.com/2011/02/16/rendering-scripts-from-partial-views-at-the-end-in-mvc/ </para>
/// </summary>
private class DelayedInjectionBlock : IDisposable {
/// <summary>
/// Unique internal storage key
/// </summary>
private const string CACHE_KEY = "DCCF8C78-2E36-4567-B0CF-FE052ACCE309"; // "DelayedInjectionBlocks";
/// <summary>
/// Internal storage identifier for remembering unique/isOnlyOne items
/// </summary>
private const string UNIQUE_IDENTIFIER_KEY = CACHE_KEY;
/// <summary>
/// What to use as internal storage identifier if no identifier provided (since we can't use null as key)
/// </summary>
private const string EMPTY_IDENTIFIER = "";
/// <summary>
/// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items
/// </summary>
/// <param name="helper">the helper from which we use the context</param>
/// <param name="identifier">optional unique sub-identifier for a given injection block</param>
/// <returns>list of delayed-execution callbacks to render internal content</returns>
public static Queue<string> GetQueue(HtmlHelper helper, string identifier = null) {
return _GetOrSet(helper, new Queue<string>(), identifier ?? EMPTY_IDENTIFIER);
}
/// <summary>
/// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items
/// </summary>
/// <param name="helper">the helper from which we use the context</param>
/// <param name="defaultValue">the default value to return if the cached item isn't found or isn't the expected type; can also be used to set with an arbitrary value</param>
/// <param name="identifier">optional unique sub-identifier for a given injection block</param>
/// <returns>list of delayed-execution callbacks to render internal content</returns>
private static T _GetOrSet<T>(HtmlHelper helper, T defaultValue, string identifier = EMPTY_IDENTIFIER) where T : class {
var storage = GetStorage(helper);
// return the stored item, or set it if it does not exist
return (T) (storage.ContainsKey(identifier) ? storage[identifier] : (storage[identifier] = defaultValue));
}
/// <summary>
/// Get the storage, but if it doesn't exist or isn't the expected type, then create a new "bucket"
/// </summary>
/// <param name="helper"></param>
/// <returns></returns>
public static Dictionary<string, object> GetStorage(HtmlHelper helper) {
var storage = helper.ViewContext.HttpContext.Items[CACHE_KEY] as Dictionary<string, object>;
if (storage == null) helper.ViewContext.HttpContext.Items[CACHE_KEY] = (storage = new Dictionary<string, object>());
return storage;
}
private readonly HtmlHelper helper;
private readonly string identifier;
private readonly string isOnlyOne;
/// <summary>
/// Create a new using block from the given helper (used for trapping appropriate context)
/// </summary>
/// <param name="helper">the helper from which we use the context</param>
/// <param name="identifier">optional unique identifier to specify one or many injection blocks</param>
/// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param>
public DelayedInjectionBlock(HtmlHelper helper, string identifier = null, string isOnlyOne = null) {
this.helper = helper;
// start a new writing context
((WebViewPage)this.helper.ViewDataContainer).OutputStack.Push(new StringWriter());
this.identifier = identifier ?? EMPTY_IDENTIFIER;
this.isOnlyOne = isOnlyOne;
}
/// <summary>
/// Append the internal content to the context's cached list of output delegates
/// </summary>
public void Dispose() {
// render the internal content of the injection block helper
// make sure to pop from the stack rather than just render from the Writer
// so it will remove it from regular rendering
var content = ((WebViewPage)this.helper.ViewDataContainer).OutputStack;
var renderedContent = content.Count == 0 ? string.Empty : content.Pop().ToString();
// if we only want one, remove the existing
var queue = GetQueue(this.helper, this.identifier);
// get the index of the existing item from the alternate storage
var existingIdentifiers = _GetOrSet(this.helper, new Dictionary<string, int>(), UNIQUE_IDENTIFIER_KEY);
// only save the result if this isn't meant to be unique, or
// if it's supposed to be unique and we haven't encountered this identifier before
if( null == this.isOnlyOne || !existingIdentifiers.ContainsKey(this.isOnlyOne) ) {
// remove the new writing context we created for this block
// and save the output to the queue for later
queue.Enqueue(renderedContent);
// only remember this if supposed to
if(null != this.isOnlyOne) existingIdentifiers[this.isOnlyOne] = queue.Count; // save the index, so we could remove it directly (if we want to use the last instance of the block rather than the first)
}
}
}
/// <summary>
/// <para>Start a delayed-execution block of output -- this will be rendered/printed on the next call to <see cref="RenderDelayed"/>.</para>
/// <para>
/// <example>
/// Print once in "default block" (usually rendered at end via <code>@Html.RenderDelayed()</code>). Code:
/// <code>
/// @using (Html.Delayed()) {
/// <b>show at later</b>
/// <span>@Model.Name</span>
/// etc
/// }
/// </code>
/// </example>
/// </para>
/// <para>
/// <example>
/// Print once (i.e. if within a looped partial), using identified block via <code>@Html.RenderDelayed("one-time")</code>. Code:
/// <code>
/// @using (Html.Delayed("one-time", isOnlyOne: "one-time")) {
/// <b>show me once</b>
/// <span>@Model.First().Value</span>
/// }
/// </code>
/// </example>
/// </para>
/// </summary>
/// <param name="helper">the helper from which we use the context</param>
/// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param>
/// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param>
/// <returns>using block to wrap delayed output</returns>
public static IDisposable Delayed(this HtmlHelper helper, string injectionBlockId = null, string isOnlyOne = null) {
return new DelayedInjectionBlock(helper, injectionBlockId, isOnlyOne);
}
/// <summary>
/// Render all queued output blocks injected via <see cref="Delayed"/>.
/// <para>
/// <example>
/// Print all delayed blocks using default identifier (i.e. not provided)
/// <code>
/// @using (Html.Delayed()) {
/// <b>show me later</b>
/// <span>@Model.Name</span>
/// etc
/// }
/// </code>
/// -- then later --
/// <code>
/// @using (Html.Delayed()) {
/// <b>more for later</b>
/// etc
/// }
/// </code>
/// -- then later --
/// <code>
/// @Html.RenderDelayed() // will print both delayed blocks
/// </code>
/// </example>
/// </para>
/// <para>
/// <example>
/// Allow multiple repetitions of rendered blocks, using same <code>@Html.Delayed()...</code> as before. Code:
/// <code>
/// @Html.RenderDelayed(removeAfterRendering: false); /* will print */
/// @Html.RenderDelayed() /* will print again because not removed before */
/// </code>
/// </example>
/// </para>
/// </summary>
/// <param name="helper">the helper from which we use the context</param>
/// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param>
/// <param name="removeAfterRendering">only render this once</param>
/// <returns>rendered output content</returns>
public static MvcHtmlString RenderDelayed(this HtmlHelper helper, string injectionBlockId = null, bool removeAfterRendering = true) {
var stack = DelayedInjectionBlock.GetQueue(helper, injectionBlockId);
if( removeAfterRendering ) {
var sb = new StringBuilder(
#if DEBUG
string.Format("<!-- delayed-block: {0} -->", injectionBlockId)
#endif
);
// .count faster than .any
while (stack.Count > 0) {
sb.AppendLine(stack.Dequeue());
}
return MvcHtmlString.Create(sb.ToString());
}
return MvcHtmlString.Create(
#if DEBUG
string.Format("<!-- delayed-block: {0} -->", injectionBlockId) +
#endif
string.Join(Environment.NewLine, stack));
}
}
Цель OP состоит в том, что он хочет определить встроенные скрипты в свой Partial View, который я предполагаю, что этот скрипт специфичен только для этого Partial View и включает этот блок в его секцию скриптов.
Я понимаю, что он хочет, чтобы Partial View был автономным. Идея аналогична компонентам при использовании Angular.
Моим способом было бы просто сохранить скрипты внутри Partial View как есть. Теперь проблема в том, что при вызове Partial View он может выполнять скрипт там перед всеми другими скриптами (который обычно добавляется к нижней части страницы макета). В этом случае у вас есть сценарий Partial View, ожидающий других скриптов. Есть несколько способов сделать это. Простейший, который я использовал ранее, использует событие на body
.
В моем макете у меня было бы что-то внизу:
// global scripts
<script src="js/jquery.min.js"></script>
// view scripts
@RenderSection("scripts", false)
// then finally trigger partial view scripts
<script>
(function(){
document.querySelector('body').dispatchEvent(new Event('scriptsLoaded'));
})();
</script>
Затем в моем частичном представлении (внизу):
<script>
(function(){
document.querySelector('body').addEventListener('scriptsLoaded', function() {
// .. do your thing here
});
})();
</script>
Другое решение - использовать стек, чтобы выталкивать все ваши скрипты и вызывать каждого в конце. Другим решением, как уже упоминалось, является шаблон RequireJS / AMD, который также отлично работает.
Следуя принципу ненавязчивого , не обязательно, чтобы «_myPartial» вводил контент непосредственно в секцию скриптов. Вы можете добавить эти сценарии частичного просмотра в отдельный файл .js
и ссылаться на них в раздел @scripts из родительского представления.
Используя Mvc Core, вы можете создать аккуратный TagHelper scripts
, как показано ниже. Это можно легко превратить в тэг section
, где вы также дадите ему имя (или имя берется из производного типа). Обратите внимание на то, что для IHttpContextAccessor
необходимо настроить инъекцию зависимостей.
При добавлении скриптов (например, в частичном)
<scripts>
<script type="text/javascript">
//anything here
</script>
</scripts>
При выводе сценариев (например, в файле макета)
<scripts render="true"></scripts>
Код
public class ScriptsTagHelper : TagHelper
{
private static readonly object ITEMSKEY = new Object();
private IDictionary<object, object> _items => _httpContextAccessor?.HttpContext?.Items;
private IHttpContextAccessor _httpContextAccessor;
public ScriptsTagHelper(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var attribute = (TagHelperAttribute)null;
context.AllAttributes.TryGetAttribute("render",out attribute);
var render = false;
if(attribute != null)
{
render = Convert.ToBoolean(attribute.Value.ToString());
}
if (render)
{
if (_items.ContainsKey(ITEMSKEY))
{
var scripts = _items[ITEMSKEY] as List<HtmlString>;
var content = String.Concat(scripts);
output.Content.SetHtmlContent(content);
}
}
else
{
List<HtmlString> list = null;
if (!_items.ContainsKey(ITEMSKEY))
{
list = new List<HtmlString>();
_items[ITEMSKEY] = list;
}
list = _items[ITEMSKEY] as List<HtmlString>;
var content = await output.GetChildContentAsync();
list.Add(new HtmlString(content.GetContent()));
}
}
}
Ну, я полагаю, что другие плакаты предоставили вам средства для непосредственного включения @section в ваш частичный (с помощью сторонних помощников html).
Но, я считаю, что если ваш скрипт тесно связан с вашим частичным, просто поместите свой javascript непосредственно внутри встроенного тега <script>
в пределах вашего частичного и сделайте с ним (просто будьте осторожны с дублированием скрипта, если вы намерены использовать частичное более одного раза в одном представлении);
Это довольно популярный вопрос, поэтому я опубликую свое решение. У меня была та же проблема, и хотя она не идеальна, я думаю, что она работает очень хорошо и не делает частичную зависимость от представления. Мой сценарий состоял в том, что действие было доступно самому, но также могло быть встроено в aa view - google map.
В моем _layout
у меня есть:
@RenderSection("body_scripts", false)
В моем index
view У меня есть:
@Html.Partial("Clients")
@section body_scripts
{
@Html.Partial("Clients_Scripts")
}
В моем представлении clients
у меня есть (все карта и ассоциативный html):
@section body_scripts
{
@Html.Partial("Clients_Scripts")
}
Мой Clients_Scripts
вид содержит javascript, который будет отображаться на странице
Таким образом, мой сценарий изолирован и может быть отображен на странице, где это необходимо, причем тэг body_scripts
отображается только в первом вхождении, что механизм просмотра бритвы находит это.
Это позволяет мне все разделять - это решение, которое хорошо работает для меня, у других могут быть проблемы с ним, но он исправляет дыры «по дизайну».
Первым решением, о котором я могу думать, является использование ViewBag для хранения значений, которые должны отображаться.
На самом деле я никогда не пробовал, если это работает с частичным представлением, но оно должно быть imo.
ViewBag.RenderScripts = new List<string>();
в верхней части главной страницы, а затем вызвало @Html.Partial("_CreateUpdatePartial",Model,ViewData)
, затем положил @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}
. В частичном представлении я положил @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}
.
– JohnLBevan
29 August 2014 в 12:55
У меня была аналогичная проблема, когда у меня была главная страница:
@section Scripts {
<script>
$(document).ready(function () {
...
});
</script>
}
...
@Html.Partial("_Charts", Model)
, но частичный вид зависел от некоторого JavaScript в разделе «Сценарии». Я решил это, закодировав частичное представление как JSON, загрузив его в переменную JavaScript, а затем используя это, чтобы заполнить div, так:
@{
var partial = Html.Raw(Json.Encode(new { html = Html.Partial("_Charts", Model).ToString() }));
}
@section Scripts {
<script>
$(document).ready(function () {
...
var partial = @partial;
$('#partial').html(partial.html);
});
</script>
}
<div id="partial"></div>
Вам не нужно использовать части с частичным представлением.
Включить в свой частичный просмотр. Он выполняет функцию после загрузки jQuery. Вы можете изменить условие условия для вашего кода.
<script type="text/javascript">
var time = setInterval(function () {
if (window.jQuery != undefined) {
window.clearInterval(time);
//Begin
$(document).ready(function () {
//....
});
//End
};
}, 10); </script>
Julio Spader
Вы можете использовать эти методы расширения: (Сохранить как PartialWithScript.cs)
namespace System.Web.Mvc.Html
{
public static class PartialWithScript
{
public static void RenderPartialWithScript(this HtmlHelper htmlHelper, string partialViewName)
{
if (htmlHelper.ViewBag.ScriptPartials == null)
{
htmlHelper.ViewBag.ScriptPartials = new List<string>();
}
if (!htmlHelper.ViewBag.ScriptPartials.Contains(partialViewName))
{
htmlHelper.ViewBag.ScriptPartials.Add(partialViewName);
}
htmlHelper.ViewBag.ScriptPartialHtml = true;
htmlHelper.RenderPartial(partialViewName);
}
public static void RenderPartialScripts(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewBag.ScriptPartials != null)
{
htmlHelper.ViewBag.ScriptPartialHtml = false;
foreach (string partial in htmlHelper.ViewBag.ScriptPartials)
{
htmlHelper.RenderPartial(partial);
}
}
}
}
}
Использовать так:
Пример частичного: (_MyPartial.cshtml) Поместите html в if и js в else.
@if (ViewBag.ScriptPartialHtml ?? true)
<p>I has htmls</p>
}
else {
<script type="text/javascript">
alert('I has javascripts');
</script>
}
В вашем _Layout.cshtml или везде, где вы хотите, чтобы скрипты из частичных объектов были визуализированы, поставьте следующий (один раз): он будет отображать только javascript всех партиций на текущей странице в этом месте.
@{ Html.RenderPartialScripts(); }
Затем, чтобы использовать ваши частичные, просто сделайте это: он отобразит только html в этом месте.
@{Html.RenderPartialWithScript("~/Views/MyController/_MyPartial.cshtml");}
Я только что добавил этот код на мой частичный вид и решил проблему, хотя и не очень чистую, она работает. Вы должны убедиться, что идентификаторы объектов, которые вы просматриваете.
$ (document) .ready (function () {$ ("# Profile_ProfileID"). Selectmenu ({icons: {button: 'ui-icon -circle-arrow-s '}}); $ ("# TitleID_FK"). selectmenu ({icons: {button:' ui-icon-circle-arrow-s '}}); $ ("# CityID_FK"). selectmenu ({icons: {button: 'ui-icon-circle-arrow-s'}}); $ ("# GenderID_FK"). selectmenu ({icons: {button: 'ui-icon-circle-arrow-s' }}); $ ("# PackageID_FK"). Selectmenu ({icons: {button: 'ui-icon-circle-arrow-s'}});});Существует фундаментальный недостаток в том, как мы думаем о сети, особенно при использовании MVC. Недостатком является то, что JavaScript как-то ответственен за представление. Представление - это представление, JavaScript (поведенческий или другой) - это JavaScript. В шаблоне MVVM от Silverlight и WPF мы сталкиваемся с «представлением первой» или «первой моделью». В MVC мы всегда должны пытаться рассуждать с точки зрения модели, и JavaScript во многом является частью этой модели.
Я бы предложил использовать шаблон AMD (мне самому нравится RequireJS ). Отделите свой JavaScript в модулях, определите свою функциональность и подключите свой html к JavaScript, вместо того чтобы полагаться на представление для загрузки JavaScript. Это очистит ваш код, разделит ваши проблемы и облегчит жизнь всем одним махом.
window
и включить библиотечные скрипты перед любыми частичными.
– crush
13 February 2015 в 02:22
Идея Плутона в лучшую сторону:
CustomWebViewPage.cs:
public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> {
public IHtmlString PartialWithScripts(string partialViewName, object model) {
return Html.Partial(partialViewName: partialViewName, model: model, viewData: new ViewDataDictionary { ["view"] = this, ["html"] = Html });
}
public void RenderScriptsInBasePage(HelperResult scripts) {
var parentView = ViewBag.view as WebPageBase;
var parentHtml = ViewBag.html as HtmlHelper;
parentView.DefineSection("scripts", () => {
parentHtml.ViewContext.Writer.Write(scripts.ToHtmlString());
});
}
}
Views \ web.config:
<pages pageBaseType="Web.Helpers.CustomWebViewPage">
Вид:
@PartialWithScripts("_BackendSearchForm")
Partial (_BackendSearchForm.cshtml):
@{ RenderScriptsInBasePage(scripts()); }
@helper scripts() {
<script>
//code will be rendered in a "scripts" section of the Layout page
</script>
}
Страница макета:
@RenderSection("scripts", required: false)
Есть способ вставить разделы в частичные виды, хотя это не очень. Вам нужно иметь доступ к двум переменным из родительского представления. Поскольку часть вашего частичного представления имеет целью создать этот раздел, имеет смысл требовать эти переменные.
Вот как выглядит вставка раздела в частичном представлении:
@model KeyValuePair<WebPageBase, HtmlHelper>
@{
Model.Key.DefineSection("SectionNameGoesHere", () =>
{
Model.Value.ViewContext.Writer.Write("Test");
});
}
И на странице, вставляющей частичный вид ...
@Html.Partial(new KeyValuePair<WebPageBase, HtmlHelper>(this, Html))
Вы также можете использовать эту технику для определения содержимого раздела программно в любом классе.
Наслаждайтесь !
У меня была аналогичная проблема, которая была решена с помощью этого:
@section ***{
@RenderSection("****", required: false)
}
Thats прекрасный способ для инъекций, я думаю.
Это помогло мне разрешить совместное размещение javascript и html для частичного просмотра в том же файле. Помогает с мыслительным процессом увидеть html и связанную с ним часть в том же файле с частичным представлением.
<div>
@Html.Partial("_MyPartialView",< model for partial view>,
new ViewDataDictionary { { "Region", "HTMLSection" } } })
</div>
@section scripts{
@Html.Partial("_MyPartialView",<model for partial view>,
new ViewDataDictionary { { "Region", "ScriptSection" } })
}
@model SomeType
@{
var region = ViewData["Region"] as string;
}
@if (region == "HTMLSection")
{
}
@if (region == "ScriptSection")
{
<script type="text/javascript">
</script">
}
Я решил этот совершенно другой маршрут (потому что я торопился и не хотел реализовывать новый HtmlHelper):
Я завернул свой Partial View в большом if-else:
@if ((bool)ViewData["ShouldRenderScripts"] == true){
// Scripts
}else{
// Html
}
Затем я дважды вызывал Partial с пользовательской ViewData:
@Html.Partial("MyPartialView", Model,
new ViewDataDictionary { { "ShouldRenderScripts", false } })
@section scripts{
@Html.Partial("MyPartialView", Model,
new ViewDataDictionary { { "ShouldRenderScripts", true } })
}
@Html.Partial("MyPartialViewScripts")
– dan richardson
24 February 2015 в 12:38
, вы можете использовать свою папку / index.cshtml в качестве главной страницы, затем добавить скрипты раздела. Затем в вашем макете есть:
@RenderSection("scripts", required: false)
и ваш index.cshtml:
@section scripts{
@Scripts.Render("~/Scripts/file.js")
}
, и он будет работать над всеми вашими частичными просмотрами. Это работает для меня
Если у вас есть законная необходимость запускать некоторые js
из partial
, вот как вы могли это сделать, требуется jQuery
:
<script type="text/javascript">
function scriptToExecute()
{
//The script you want to execute when page is ready.
}
function runWhenReady()
{
if (window.$)
scriptToExecute();
else
setTimeout(runWhenReady, 100);
}
runWhenReady();
</script>
setTimeout(SeeIfReady, 100)
тоже работает, нет необходимости в неявной оценке ...
– drzaus
13 February 2014 в 20:32
У меня была эта проблема, и я использовал эту технику .
Это лучшее решение, которое я нашел очень гибким.
Также, пожалуйста, проголосуйте здесь , чтобы добавить поддержку декларации кумулятивного раздела
предположим, что у вас есть частичный вид, называемый _contact.cshtml, ваш контакт может быть законным (имя) или физическим субъектом (имя, фамилия). ваш взгляд должен заботиться о том, что отображается, и что может быть достигнуто с помощью javascript. поэтому может потребоваться отложенный рендеринг и внутренний вид JS.
единственный способ, о котором я думаю, о том, как его можно опустить, - это когда мы создаем ненавязчивый способ обращения с такими проблемами пользовательского интерфейса.
также обратите внимание, что MVC 6 будет иметь так называемый компонент View, даже фьючерсы MVC имели некоторые подобные вещи, и Telerik также поддерживает такую вещь ...
isOnlyOne
, но только если вы хотите отобразить его в определенном месте по имени, вы предоставляете идентификатор, иначе он будет сброшен вHtml.RenderDelayed()
. – drzaus 14 August 2015 в 17:46