We often need to access the value of some server-side variable in our client-side code. For example, we use the value site.uri.public
throughout our Javascript code when building urls for AJAX requests. The value of this variable is taken directly from UserFrosting's configuration variable of the same name, and embedded into a Javascript variable on every page.
There are a number of ways to get the values of specific variables from the server to the client. For example:
.prop
, .val
, .data
, or some other Javascript method<script>
tag and assigning it to a Javascript variableDepending on the context of your application, one of these options might be more appropriate than the others. For the purposes of this section however, we will focus on the last option, and the "global" Javascript variables that UserFrosting makes available on each page. This is the best choice when you have miscellaneous values that aren't closely associated with any specific element of the DOM, and are generic enough that they don't warrant the trouble of making a separate AJAX request for their values.
No matter how you go about it, it's important to keep in mind that you're not directly accessing server-side variables in Javascript. Keep in mind our discussion on the client-server conversation - the client makes a request for the page and receives a fixed piece of HTML in the response.
Any PHP variables we receive in the response are really just copies of their server-side values when the page was rendered. Our page can't change these values on the server without making another request - and even then, changes won't persist from one request to the next unless we use some sort of persistence mechanism (sessions, database, etc).
By default, UserFrosting will create a global site
Javascript variable on every page. site
is a JSON object that contains the following values:
site.uri.public
: Base url of your site (e.g., https://example.com
).site.debug.ajax
: true
if AJAX debugging mode is enabled, false otherwise.site.csrf.keys.name
: The name of the CSRF name
attribute expected by the CSRF middleware (Defaults to csrf_name
).site.csrf.keys.value
: The name of the CSRF value
attribute expected by the CSRF middleware (Defaults to csrf_value
).site.csrf.name
: The value of the CSRF name
attribute.site.csrf.value
: The value of the CSRF value
attribute.Notice that all of these variables are nested under a single, top-level site
object which is constructed in the core/templates/pages/partials/config.js.twig
template. By formatting these as keys in a JSON object, rather than making each one an individual variable, we avoid polluting Javascript's global namespace with too many identifiers.
To add, remove, or modify the contents of the site
object, simply override config.js.twig
in your Sprinkle. config.js.twig
itself pulls its values from Twig's global variables (site
, current_user
, etc). Keep in mind that you can add global variables to Twig by creating a Twig extension and then loading your extension by extending the view
service. This process is summarized in this diagram:
Remember, any data you place in the
site
variable will be visible to the end-user - all they have to do is "View source"! Don't put any sensitive or private information in this variable.
For your convenience, the core/templates/pages/partials/page.js.twig
template will generate a page
JSON object. This is similar to site
, but is populated by passing an array of data to the page
key when rendering a template:
// In account/src/Controller/AccountController.php
public function pageRegister($request, $response, $args)
{
// ...
// Load validation rules
$schema = new RequestSchema("schema://requests/register.yaml");
$validatorRegister = new JqueryValidationAdapter($schema, $this->ci->translator);
// Pass them to the `page` key of the template placeholders
return $this->ci->view->render($response, 'pages/register.html.twig', [
"page" => [
"validators" => [
"register" => $validatorRegister->rules()
]
]
]);
}
You can then include the page.js.twig
component in your page template's scripts_page
block, before any page-specific asset bundles:
{# In account/templates/pages/register.html.twig #}
{% block scripts_page %}
<!-- Include page-specific variables -->
<script>
{% include "pages/partials/page.js.twig" %}
</script>
<!-- Include page-specific asset bundles -->
{{ assets.js('js/pages/register') | raw }}
{% endblock %}
Twig will automatically convert the array you passed to render
in your page controller method to a JSON object:
// Appears in the rendered page DOM for /account/register
var page = {
"validators": {
"register": {
"rules": {
"user_name": {
"rangelength": [
1,
50
],
"noLeadingWhitespace": true,
"noTrailingWhitespace": true,
"required": true,
"username": true
},
...
},
"messages": {
"user_name": {
"rangelength": "Username must be between 1 and 50 characters in length.",
"noLeadingWhitespace": "The value for 'Username' cannot begin with spaces, tabs, or other whitespace.",
"noTrailingWhitespace": "The value for 'Username' cannot end with spaces, tabs, or other whitespace.",
"required": "Please specify a value for 'Username'.",
"username": "Username may consist only of lowercase letters, numbers, '.', '-', and '_'."
},
...
}
}
}
};
Occasionally, you will want to dynamically modify the contents of site
, page
, or some other JSON variable. For example, you might want to override a variable on a specific page. To do this, you can use jQuery's extend
method:
<script>
site = $.extend(
true, // deep extend
{
"debug" : {
"ajax" : false // Disable AJAX debugging on this page only
}
},
site
);
</script>
You should do this in your page template's scripts_page
block, after loading the original versions of the variables but before loading any page asset bundles.