在Symfony 3中自动包含动作专用的JS / CSS

I often have some CSS rules and JavaScript functions that are only used in a single Twig file that is rendered by a single controller action. I use seperate files for those CSS rules and JavaScript functions and structured them like this:

Bundle File Structure

I have one controller Jd34TestController in this bundle that contains the frontAction and requestAction, rendering front.html.twig and request.html.twig. Both Twig files include one common Twig file, app/Resources/views/base.html.twig. I am looking for a way to automatically (magically) include Jd34Test/front.js and Jd34Test/front.css when Jd34Test/front.html.twig is rendered and the same for the request action and any other action. If any of these css/js files doesn't exist, it should skip that including and don't throw exceptions.

What is the best approach to automate this? I tried using Twig_Extension functions and macros, but it seems too risky to guess the css/js paths based on the return value of $this->requestStack->getCurrentRequest()->get('_controller').

I work on huge web apps and I basically use the same structure as you where I only include JS and CSS files in twig files that need them. Sometimes you find that a certain JS plugin is only needed on a certain page and not on others, as such it only makes sense to insert it only on the page that requires it. This is what I do.

Depending on how big, in terms of features and pages, the app is; I can have a single base.html.twig file and multilple layout.html.twig files that extends base.html.twig file. This way, base.html.twig only has the css and js files that are required by all layout.html.twig files. Then each layout.html.twig file includes JS and CSS files required by all files that will extend it. Say you have three pages that extend a certain layout.html.twig file, they will all have the JS and CSS of that layout.html.twig file and each can add extra JS and CSS required.

Here is how I do it:

base.html.twig would look something like this:

<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8 no-js"> <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9 no-js"> <![endif]-->
<!--[if !IE]><!-->
<html lang="en" class="no-js">
<!--<![endif]-->
<!-- BEGIN HEAD -->
<head>
    <meta charset="utf-8">
    {% block mainPageTitle %}
        <title>Snappic | Photobooth Software</title>
    {% endblock %}
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta content="width=device-width, initial-scale=1" name="viewport">
    <meta content="" name="description">
    <meta content="" name="author">

    <!-- BEGIN GLOBAL MANDATORY STYLES -->
    {% stylesheets
    "bundles/snappicadmin/css/layout/components-md.css"
    "bundles/snappicadmin/plugins/bootstrap/css/bootstrap.css"
    "bundles/snappicadmin/plugins/web-icons/web-icons.min.css"
    "bundles/snappicadmin/css/layout/layout.css"%}

    <link rel="stylesheet" href="{{ asset_url }}" />

    {% endstylesheets %}

    <!-- END GLOBAL MANDATORY STYLES -->

    <!-- BEGIN PAGE SPECIFIC STYLES -->

    {% block pageCSS %}
    {% endblock %}

    <!-- END PAGE STYLES -->


    <link rel="icon" href="{{ asset('bundles/snappicadmin/images/icon/plain_logo-32x32.png') }}" sizes="32x32" />

</head>
<!-- END HEAD -->
<!-- BEGIN BODY -->

<body class="page-md">

    <!-- GENERAL LAYOUT CONTENT HERE e.g Main menu -->


    {% block content %}
    {% endblock %}

<!-- BEGIN JAVASCRIPTS -->
<!-- BEGIN CORE PLUGINS -->
<!--[if lt IE 9]>
{% javascripts
"@SnappicAdminBundle/Resources/public/plugins/respond.min.js"
"@SnappicAdminBundle/Resources/public/plugins/excanvas.min.js"%}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
<![endif]-->

{% javascripts
"@SnappicAdminBundle/Resources/public/plugins/fullcalendar/lib/moment.min.js"
"@SnappicAdminBundle/Resources/public/plugins/jquery.browser.min.js"
"@SnappicAdminBundle/Resources/public/js/utilities/utilities.js"
"@SnappicAdminBundle/Resources/public/plugins/jquery.min.js"%}

<script src="{{ asset_url }}"></script>

{% endjavascripts %}


{% block pagescript %}
{% endblock %}


<script>

    jQuery(document).ready(function() {
        App.init(); 
        Layout.init(); 

        initSlideOut();
    });
</script>
<!-- END JAVASCRIPT -->
</body>
<!-- END BODY -->
</html>

Notice the {% block pageCSS %}, this is where your page specific CSS will go and same for JS in {% block pagescript %}.

Here is how a page that extends base.html.twig would look like:

{% extends "@SnappicAdmin/Layout/base.html.twig" %}

{% block mainPageTitle %}<title>Snappic - Dashboard</title>{% endblock %}

{% block pageCSS %}
    {% stylesheets
    "bundles/snappicadmin/css/layout/plugins.min.css"
    "@SnappicAdminBundle/Resources/public/css/dashboard/dashboard.css"%}

    <link rel="stylesheet" href="{{ asset_url }}" />

    {% endstylesheets %}
{% endblock %}

 {% block content %}
     <!-- BEGIN PAGE CONTENT INNER -->
        This is where your page content goes
     <!-- END PAGE CONTENT INNER -->
{% endblock %}

 {% block pagescript %}
     {% javascripts
     "bundles/snappicadmin/plugins/fullcalendar/lib/moment.min.js"
     "@SnappicAdminBundle/Resources/public/js/dashboard/dashboard.js"
     %}

     <script src="{{ asset_url }}"></script>

     {% endjavascripts %}

 {% endblock %}

The key here is to use [Template Inheritance (Twig inheritance)][1] by defining blocks. It will make your code much more manageable, not to mention better rendering.

I believe this is much easier and cleaner than injecting the files via the controller.

PS. Sorry for including so many files, I wanted to make it as clearer as possible, if anything is unclear, give me a shout.

Happy clean coding!