asp.net-mvc cargar - Inyectar contenido en secciones específicas desde una vista parcial ASP.NET MVC 3 con Razor View Engine




modal ventana (19)

Tengo esta sección definida en mi _Layout.cshtml

@RenderSection("Scripts", false)

Puedo usarlo fácilmente desde una vista:

@section Scripts { 
    @*Stuff comes here*@
}

Con lo que estoy luchando es cómo obtener contenido inyectado dentro de esta sección desde una vista parcial.

Supongamos que esta es mi página de vista:

@section Scripts { 

    <script>
        //code comes here
    </script>
}

<div>
    poo bar poo
</div>

<div>
  @Html.Partial("_myPartial")
</div>

Necesito inyectar algo de contenido dentro de la sección de _myPartial la vista parcial de _myPartial .

¿Cómo puedo hacer esto?


Answers

Hay una falla fundamental en la forma en que pensamos acerca de la web, especialmente cuando usamos MVC. La falla es que JavaScript es de alguna manera la responsabilidad de la vista. Una vista es una vista, JavaScript (de comportamiento o de otro tipo) es JavaScript. En Silverlight y el patrón MVVM de WPF, nos enfrentamos a "ver primero" o "modelar primero". En MVC siempre debemos tratar de razonar desde el punto de vista del modelo y JavaScript es una parte de este modelo de muchas maneras.

Sugeriría usar el patrón AMD (a mí me gusta RequireJS ). Separe su JavaScript en módulos, defina su funcionalidad y conéctese a su html desde JavaScript en lugar de confiar en una vista para cargar el JavaScript. Esto limpiará su código, separará sus inquietudes y le facilitará la vida de una sola vez.


Tuve el mismo problema resuelto con esto:

@section ***{
@RenderSection("****", required: false)
}

Esa es una forma bonita de inyectar, supongo.


La primera solución que se me ocurre es usar ViewBag para almacenar los valores que se deben representar.

Una vez, nunca intenté si esto funciona desde una vista parcial, pero debería imo.


Si tiene una necesidad legítima de ejecutar algunos js de forma partial , aquí le explicamos cómo podría hacerlo, jQuery es obligatorio:

<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>

Esta es una pregunta bastante popular, así que publicaré mi solución.
Tuve el mismo problema y, aunque no es lo ideal, creo que funciona bastante bien y que no depende en parte de la vista.
Mi escenario era que una acción era accesible por sí misma, pero también podía integrarse en una vista: un mapa de Google.

En mi _layout tengo:

@RenderSection("body_scripts", false)

En mi vista de index tengo:

@Html.Partial("Clients")
@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

En la vista de mis clients tengo (todo el mapa y assoc. Html):

@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

La vista My Clients_Scripts contiene el javascript que se representará en la página.

De esta manera, mi secuencia de comandos se aísla y puede representarse en la página donde sea necesario, con la etiqueta body_scripts solo representada en la primera aparición que el motor de la vista de la máquina de afeitar encuentra.

Eso me permite tener todo separado: es una solución que funciona bastante bien para mí, otros pueden tener problemas con eso, pero parchea el agujero "por diseño".


La idea de Plutón de una manera más agradable:

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());
        });
    }
}

Vistas \ web.config:

<pages pageBaseType="Web.Helpers.CustomWebViewPage">

Ver:

@PartialWithScripts("_BackendSearchForm")

Parcial (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

@helper scripts() {
<script>
    //code will be rendered in a "scripts" section of the Layout page
</script>
}

Página de diseño:

@RenderSection("scripts", required: false)

Bueno, supongo que los otros carteles te han proporcionado un medio para incluir directamente una @sección dentro de tu parcial (mediante el uso de terceros ayudantes html).

Pero, creo que, si su script está estrechamente unido a su parcial, simplemente coloque su javascript directamente dentro de una etiqueta en línea <script> dentro de su parcial y termine con él (tenga cuidado con la duplicación de scripts si tiene la intención de usar el parcial más de una vez en una sola vista);


No puedes necesitar usar secciones en vista parcial.

Incluir en su Vista Parcial. Ejecuta la función después de jQuery cargado. Puede alterar la cláusula de condición de su código.

<script type="text/javascript">    
var time = setInterval(function () {
    if (window.jQuery != undefined) {
        window.clearInterval(time);

        //Begin
        $(document).ready(function () {
           //....
        });
        //End
    };
}, 10); </script>

Julio spader


Usando Mvc Core puede crear una secuencia de scripts TagHelper ordenada como se ve a continuación. Esto podría transformarse fácilmente en una etiqueta de section donde también le da un nombre (o el nombre se toma del tipo derivado). Tenga en cuenta que la inyección de dependencias debe configurarse para IHttpContextAccessor .

Al agregar scripts (por ejemplo, en un parcial)

<scripts>
    <script type="text/javascript">
        //anything here
    </script>
</scripts>

Al generar los scripts (por ejemplo, en un archivo de diseño)

<scripts render="true"></scripts>

Código

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()));
            }
        }
    }

Supongamos que tiene una vista parcial llamada _contact.cshtml, su contacto puede ser legal (nombre) o un sujeto físico (nombre, apellido). su vista debe tener cuidado con lo que se representa y que se puede lograr con javascript. por lo que puede ser necesaria la reproducción retrasada y la vista interior JS.

La única forma en que pienso, cómo se puede omitir, es cuando creamos una forma discreta de manejar tales preocupaciones de UI.

También tenga en cuenta que MVC 6 tendrá un llamado componente de visualización, incluso los futuros de MVC tenían algunas cosas similares y Telerik también admite tal cosa ...


Siguiendo el principio unobtrusive , no es muy necesario que "_myPartial" inyecte contenido directamente en la sección de scripts. Puede agregar esos scripts de vista parcial en un archivo .js separado y consultarlos en la sección @scripts de la vista principal.


Tuve un problema similar, donde tuve una página maestra de la siguiente manera:

@section Scripts {
<script>
    $(document).ready(function () {
        ...
    });
</script>
}

...

@Html.Partial("_Charts", Model)

pero la vista parcial dependía de algún JavaScript en la sección de Scripts. Lo resolví codificando la vista parcial como JSON, cargándola en una variable de JavaScript y luego usé esto para rellenar un div, así que:

@{
    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>

Las secciones no funcionan en vistas parciales y eso es por diseño. Puede usar algunos ayudantes personalizados para lograr un comportamiento similar, pero honestamente es responsabilidad de la vista incluir los scripts necesarios, no la responsabilidad parcial. Recomendaría usar la sección @scripts de la vista principal para hacer eso y no preocuparse por los parciales de los scripts.


Puede usar estos métodos de extensión : (Guardar como 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);
                }
            }
        }
    }
}

Use así:

Ejemplo parcial: (_MyPartial.cshtml) Ponga el html en el if, y el js en el else.

@if (ViewBag.ScriptPartialHtml ?? true)
    <p>I has htmls</p>
}
else {
    <script type="text/javascript">
        alert('I has javascripts');
    </script>
}

En su _Layout.cshtml, o donde quiera que se representen los scripts de los parciales, coloque lo siguiente (una vez): solo mostrará el javascript de todos los parciales en la página actual en esta ubicación.

@{ Html.RenderPartialScripts(); }

Luego, para usar su parcial, simplemente haga esto: solo mostrará el html en esta ubicación.

@{Html.RenderPartialWithScript("~/Views/MyController/_MyPartial.cshtml");}

Resolví esta ruta completamente diferente (porque tenía prisa y no quería implementar un nuevo HtmlHelper):

Envolví mi vista parcial en una gran declaración if-else:

@if ((bool)ViewData["ShouldRenderScripts"] == true){
// Scripts
}else{
// Html
}

Luego, llamé al Parcial dos veces con un ViewData personalizado:

@Html.Partial("MyPartialView", Model, 
    new ViewDataDictionary { { "ShouldRenderScripts", false } })

@section scripts{
    @Html.Partial("MyPartialView", Model, 
        new ViewDataDictionary { { "ShouldRenderScripts", true } })
}

Acabo de agregar este código en mi vista parcial y resolví el problema, aunque no está muy limpio, funciona. Debe asegurarse de los identificadores de los objetos que está representando.

$ (documento) .ready (función () {$ ("# 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'}});});

El objetivo del OP es que quiere definir los guiones en línea en su Vista parcial, que asumo que este guión es específico solo para esa Vista parcial, y que ese bloque esté incluido en su sección de guiones.

Entiendo que él quiere que esa Vista Parcial sea autosuficiente. La idea es similar a los componentes cuando se usa Angular.

Mi manera sería simplemente mantener los scripts dentro de la Vista Parcial tal como están. Ahora el problema con eso es cuando se llama Vista parcial, puede ejecutar el script allí antes que todos los demás scripts (que normalmente se agregan al final de la página de diseño). En ese caso, solo tiene que esperar la secuencia de comandos de Vista parcial para las otras secuencias de comandos. Hay varias formas de hacerlo. El más simple, que he usado antes, es usar un evento en el body .

En mi diseño, tendría algo en la parte inferior como esto:

// 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>

Luego en mi Vista Parcial (en la parte inferior):

<script>
  (function(){
    document.querySelector('body').addEventListener('scriptsLoaded', function() {

      // .. do your thing here

    });
  })();
</script>

Otra solución es usar una pila para empujar todos sus scripts y llamar a cada uno al final. Otra solución, como ya se mencionó, es el patrón RequireJS / AMD, que también funciona muy bien.


Tuve este problema y utilicé this técnica.

Es la mejor solución que he encontrado que es muy flexible.

También vote here para agregar soporte para la declaración de la sección acumulativa


Compruebe este SharpDOM . Este es un dsl interno ac # 4.0 para generar html y también el motor de visualización mvc de asp.net.







asp.net asp.net-mvc asp.net-mvc-3 razor partial-views