una subir plantillas plantilla pagina los insertar guardar existente editar crear como cambios html asp.net-mvc asp.net-mvc-3 razor

html - subir - Usar secciones en las plantillas Editor/Pantalla



mailchimp html (8)

Quiero mantener todo mi código JavaScript en una sección; justo antes de la etiqueta de cierre del body en la página de diseño principal y me pregunto cuál es la mejor manera de hacerlo, estilo MVC.

Por ejemplo, si creo un archivo DisplayTemplate/DateTime.cshtml que utiliza el DisplayTemplate/DateTime.cshtml y DisplayTemplate/DateTime.cshtml jQuery UI, insertaría el JavaScript directamente en esa plantilla, pero luego se mostrará en la mitad de la página.

En mis vistas normales, puedo usar @section JavaScript { //js here } y luego @RenderSection("JavaScript", false) en mi diseño maestro, pero parece que esto no funciona en las plantillas de visualización / edición, ¿alguna idea?


@Darin Dimitrov y @ eth0 respuestas para usar con el uso de extensión de paquete:

@Html.Resources(a => new HelperResult(b => b.Write( System.Web.Optimization.Scripts.Render("~/Content/js/formBundle").ToString())), "jsTop")


Enfrenté el mismo problema, pero las soluciones propuestas aquí funcionan bien solo para agregar referencia al recurso y no son muy adecuadas para el código JS en línea. Encontré un article muy útil y envolví todos mis JS en línea (y también etiquetas de guiones) en

@using (Html.BeginScripts()) { <script src="@Url.Content("~/Scripts/jquery-ui-1.8.18.min.js")" type="text/javascript"></script> <script> // my inline scripts here </script> }

Y en la vista _Layout colocada @Html.PageScripts() justo antes de cerrar la etiqueta ''cuerpo''. Funciona como un encanto para mí.

Los ayudantes mismos:

public static class HtmlHelpers { private class ScriptBlock : IDisposable { private const string scriptsKey = "scripts"; public static List<string> pageScripts { get { if (HttpContext.Current.Items[scriptsKey] == null) HttpContext.Current.Items[scriptsKey] = new List<string>(); return (List<string>)HttpContext.Current.Items[scriptsKey]; } } WebViewPage webPageBase; public ScriptBlock(WebViewPage webPageBase) { this.webPageBase = webPageBase; this.webPageBase.OutputStack.Push(new StringWriter()); } public void Dispose() { pageScripts.Add(((StringWriter)this.webPageBase.OutputStack.Pop()).ToString()); } } public static IDisposable BeginScripts(this HtmlHelper helper) { return new ScriptBlock((WebViewPage)helper.ViewDataContainer); } public static MvcHtmlString PageScripts(this HtmlHelper helper) { return MvcHtmlString.Create(string.Join(Environment.NewLine, ScriptBlock.pageScripts.Select(s => s.ToString()))); } }


Esta publicación realmente me ayudó, así que pensé que publicaría mi implementación de la idea básica. Introduje una función auxiliar que puede devolver etiquetas de script para su uso en la función @ Html.Resource.

También agregué una clase estática simple para poder usar variables tipadas para identificar un recurso JS o CSS.

public static class ResourceType { public const string Css = "css"; public const string Js = "js"; } public static class HtmlExtensions { public static IHtmlString Resource(this HtmlHelper htmlHelper, Func<object, dynamic> template, string Type) { if (htmlHelper.ViewContext.HttpContext.Items[Type] != null) ((List<Func<object, dynamic>>)htmlHelper.ViewContext.HttpContext.Items[Type]).Add(template); else htmlHelper.ViewContext.HttpContext.Items[Type] = new List<Func<object, dynamic>>() { template }; return new HtmlString(String.Empty); } public static IHtmlString RenderResources(this HtmlHelper htmlHelper, string Type) { if (htmlHelper.ViewContext.HttpContext.Items[Type] != null) { List<Func<object, dynamic>> resources = (List<Func<object, dynamic>>)htmlHelper.ViewContext.HttpContext.Items[Type]; foreach (var resource in resources) { if (resource != null) htmlHelper.ViewContext.Writer.Write(resource(null)); } } return new HtmlString(String.Empty); } public static Func<object, dynamic> ScriptTag(this HtmlHelper htmlHelper, string url) { var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext); var script = new TagBuilder("script"); script.Attributes["type"] = "text/javascript"; script.Attributes["src"] = urlHelper.Content("~/" + url); return x => new HtmlString(script.ToString(TagRenderMode.Normal)); } }

Y en uso

@Html.Resource(Html.ScriptTag("Areas/Admin/js/plugins/wysiwyg/jquery.wysiwyg.js"), ResourceType.Js)

Gracias a @Darin Dimitrov, que proporcionó la respuesta en mi pregunta aquí .


Instale el paquete Forloop.HtmlHelpers Forloop.HtmlHelpers: agrega algunos ayudantes para administrar scripts en vistas parciales y plantillas de editor.

En algún lugar de su diseño, debe llamar

@Html.RenderScripts()

Aquí será donde se generarán los archivos de script y los bloques de scripts en la página, por lo que recomendaría ponerlos después de los scripts principales en el diseño y después de una sección de scripts (si tiene uno).

Si está utilizando The Web Optimization Framework con bundling, puede usar la sobrecarga

@Html.RenderScripts(Scripts.Render)

para que este método se use para escribir los archivos de script.

Ahora, cada vez que desee agregar bloques de comandos o bloques en una vista, vista parcial o plantilla, simplemente use

@using (Html.BeginScriptContext()) { Html.AddScriptFile("~/Scripts/jquery.validate.js"); Html.AddScriptBlock( @<script type="text/javascript"> $(function() { $(''#someField'').datepicker(); }); </script> ); }

Los ayudantes garantizan que solo se represente una referencia de archivo de script si se agrega varias veces y también garantiza que los archivos de script se procesen en un orden esperado, es decir,

  1. Diseño
  2. Parciales y plantillas (en el orden en que aparecen en la vista, de arriba a abajo)

La respuesta dada en Rellenar una sección de maquinilla de afeitar de un uso parcial de RequireScript HtmlHelper sigue el mismo patrón. También tiene el beneficio de que comprueba y suprime las referencias duplicadas a la misma URL Javascript, y tiene un parámetro de priority explícito que se puede utilizar para controlar el orden.

Extendí esta solución agregando métodos para:

// use this for scripts to be placed just before the </body> tag public static string RequireFooterScript(this HtmlHelper html, string path, int priority = 1) { ... } public static HtmlString EmitRequiredFooterScripts(this HtmlHelper html) { ... } // use this for CSS links public static string RequireCSS(this HtmlHelper html, string path, int priority = 1) { ... } public static HtmlString EmitRequiredCSS(this HtmlHelper html) { ... }

Sin embargo, me gustan las soluciones de Darin y eth0 ya que usan la plantilla HelperResult , que permite secuencias de comandos y bloques CSS, no solo enlaces a archivos Javascript y CSS.


Me gustó la solution publicada por @ john-w-harding, así que la combiné con la answer de @ darin-dimitrov para hacer la siguiente solución probablemente demasiado complicada que permite retrasar la renderización de html (scripts también) dentro de un bloque de uso.

USO

En una vista parcial repetida, solo incluya el bloque una vez:

@using (Html.Delayed(isOnlyOne: "MYPARTIAL_scripts")) { <script> someInlineScript(); </script> }

En una vista parcial (¿repetida?), Incluya el bloque para cada vez que se use el parcial:

@using (Html.Delayed()) { <b>show me multiple times, @Model.Whatever</b> }

En una vista parcial (¿repetida?), Incluya el bloque una vez y luego recíbalo específicamente por su nombre one-time :

@using (Html.Delayed("one-time", isOnlyOne: "one-time")) { <b>show me once by name</b> <span>@Model.First().Value</span> }

Para renderizar:

@Html.RenderDelayed(); // the "default" unidentified blocks @Html.RenderDelayed("one-time", false); // render the specified block by name, and allow us to render it again in a second call @Html.RenderDelayed("one-time"); // render the specified block by name @Html.RenderDelayed("one-time"); // since it was "popped" in the last call, won''t render anything

CÓDIGO

public static class HtmlRenderExtensions { /// <summary> /// Delegate script/resource/etc injection until the end of the page /// <para>@via https://.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)); } }


Podrías proceder con una conjunción de dos ayudantes:

public static class HtmlExtensions { public static MvcHtmlString Script(this HtmlHelper htmlHelper, Func<object, HelperResult> template) { htmlHelper.ViewContext.HttpContext.Items["_script_" + Guid.NewGuid()] = template; return MvcHtmlString.Empty; } public static IHtmlString RenderScripts(this HtmlHelper htmlHelper) { foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys) { if (key.ToString().StartsWith("_script_")) { var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func<object, HelperResult>; if (template != null) { htmlHelper.ViewContext.Writer.Write(template(null)); } } } return MvcHtmlString.Empty; } }

y luego en su _Layout.cshtml :

<body> ... @Html.RenderScripts() </body>

y en algún lugar de alguna plantilla:

@Html.Script( @<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script> )


Versión modificada de la respuesta de Darin para garantizar el orden. También funciona con CSS:

public static IHtmlString Resource(this HtmlHelper HtmlHelper, Func<object, HelperResult> Template, string Type) { if (HtmlHelper.ViewContext.HttpContext.Items[Type] != null) ((List<Func<object, HelperResult>>)HtmlHelper.ViewContext.HttpContext.Items[Type]).Add(Template); else HtmlHelper.ViewContext.HttpContext.Items[Type] = new List<Func<object, HelperResult>>() { Template }; return new HtmlString(String.Empty); } public static IHtmlString RenderResources(this HtmlHelper HtmlHelper, string Type) { if (HtmlHelper.ViewContext.HttpContext.Items[Type] != null) { List<Func<object, HelperResult>> Resources = (List<Func<object, HelperResult>>)HtmlHelper.ViewContext.HttpContext.Items[Type]; foreach (var Resource in Resources) { if (Resource != null) HtmlHelper.ViewContext.Writer.Write(Resource(null)); } } return new HtmlString(String.Empty); }

Puede agregar recursos JS y CSS como este:

@Html.Resource(@<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>, "js") @Html.Resource(@<link rel="stylesheet" href="@Url.Content("~/CSS/style.css")" />, "css")

Y renderice los recursos de JS y CSS de esta manera:

@Html.RenderResources("js") @Html.RenderResources("css")

Podría hacer una verificación de cadena para ver si comienza con script / link para que no tenga que definir explícitamente qué es cada recurso.