@@ -113,19 +113,35 @@ Tags
113
113
``urls_to_js ``
114
114
~~~~~~~~~~~~~~
115
115
116
- This tag spits out a JavaScript object that can be used in the same manner as Django's URL
117
- `reverse <https://docs.djangoproject.com/en/3.1/ref/urlresolvers/#reverse >`_ function.
116
+ Often client side JavaScript needs to fetch site URLs asynchronously. These instances either
117
+ necessitate using dynamic templating to reverse the url via the `url ` tag or to hardcode the path
118
+ into the JavaScript thereby violating the DRY principle. Frequently the need to generate these paths
119
+ are the only thing driving the need to generate the JavaScript dynamically. But these paths might
120
+ change only at deployment, not runtime, so the better approach is to generate JavaScript at
121
+ deployment time and serve it statically. This tag makes that process even easier by automatically
122
+ translating the site's url configuration into a JavaScript utility that can be used in the same
123
+ manner as Django's URL `reverse <https://docs.djangoproject.com/en/3.1/ref/urlresolvers/#reverse >`_
124
+ function.
118
125
119
126
It accepts a number of different parameters:
120
127
128
+ - **visitor ** A string import path or a class that implements `URLTreeVisitor `. The visitor
129
+ walks the URL tree and generates the JavaScript, users may customize the JavaScript generated
130
+ by implementing their own visitor class. Two visitors are included. The default,
131
+ `SimpleURLWriter `, spits out an object structure that indexes paths by their namespaces. The
132
+ `ClassURLWriter `, spits out ES5 or 6 classes that provide a `reverse ` function directly
133
+ analogous to Django's reverse function.
121
134
- **url_conf ** The root url module to dump urls from. Can be an import string or an actual
122
135
module type. default: settings.ROOT_URLCONF
123
- - **indent ** String to use for indentation in javascript, default: '\\ t'
136
+ - **indent ** String to use for indentation in javascript, default: '\\ t', If None or the empty
137
+ string is specified, the generated code will not contain newlines.
124
138
- **depth ** The starting indentation depth, default: 0
125
139
- **include ** A list of path names to include, namespaces without path names will be treated as
126
140
every path under the namespace. Default: include everything
127
141
- **exclude ** A list of path names to exclude, namespaces without path names will be treated as
128
142
every path under the namespace. Excludes override includes. Default: exclude nothing
143
+ - **raise_on_not_found ** If True (default), the generated JavaScript will raise a TypeError if
144
+ asked to reverse an unrecognized URL name or set of arguments.
129
145
- **es5 ** If True, dump es5 valid JavaScript, if False JavaScript will be es6, default: False
130
146
131
147
Includes and excludes are hierarchical strings that contain the fully qualified name of a namespace
@@ -136,6 +152,12 @@ not include paths directly under `namespace1`. Excludes always override includes
136
152
path is included and no paths are excluded. If any includes are provided, then only those includes
137
153
are included (everything else is by default excluded).
138
154
155
+ .. note ::
156
+
157
+ When implementing custom URL visitors, any additional named arguments passed to the `urls_to_js `
158
+ tag will be passed as kwargs to the URL visitor when this tag instantiates it. These parameters
159
+ are meant to provide configuration toggles for the generated JavaScript.
160
+
139
161
.. warning ::
140
162
141
163
All the URLs embedded in JavaScript are exposed client side. Its never a good idea to have site
@@ -240,3 +262,60 @@ Paths with unnamed arguments are also supported, but be kind to yourself and don
240
262
Any number of placeholders may be registered against any number of variable/app_name combinations.
241
263
When `urls_to_js ` is run it won't give up until its tried all placeholders that might potentially
242
264
match the path.
265
+
266
+ `ClassURLWriter `
267
+ ****************
268
+
269
+ A visitor class that produces ES5/6 JavaScript class is now included. This class is not used by
270
+ default, but should be the preferred visitor for larger, more complex URL trees - mostly because
271
+ it minifies better than the default `SimpleURLWriter `. To use the class writer::
272
+
273
+ {% urls_to_js visitor='render_static.ClassURLWriter' class_name='URLReverser' %}
274
+
275
+ This will generate an ES6 class by default::
276
+
277
+ class URLReverser {
278
+
279
+ match(kwargs, args, expected) {
280
+ if (Array.isArray(expected)) {
281
+ return Object.keys(kwargs).length === expected.length &&
282
+ expected.every(value => kwargs.hasOwnProperty(value));
283
+ } else if (expected) {
284
+ return args.length === expected;
285
+ } else {
286
+ return Object.keys(kwargs).length === 0 && args.length === 0;
287
+ }
288
+ }
289
+
290
+ reverse(qname, kwargs={}, args=[]) {
291
+ let url = this.urls;
292
+ for (const ns of qname.split(':')) {
293
+ if (ns && url) { url = url.hasOwnProperty(ns) ? url[ns] : null; }
294
+ }
295
+ if (url) {
296
+ let pth = url(kwargs, args);
297
+ if (typeof pth === "string") { return pth; }
298
+ }
299
+ throw new TypeError(`No reversal available for parameters at path: ${qname}`);
300
+ }
301
+
302
+ urls = {
303
+ "simple": (kwargs={}, args=[]) => {
304
+ if (this.match(kwargs, args)) { return "/simple/"; }
305
+ if (this.match(kwargs, args, ['arg1'])) { return `/simple/${kwargs["arg1"]}`; }
306
+ },
307
+ "different": (kwargs={}, args=[]) => {
308
+ if (this.match(kwargs, args, ['arg1','arg2'])) {
309
+ return `/different/${kwargs["arg1"]}/${kwargs["arg2"]}`;
310
+ }
311
+ },
312
+ }
313
+ };
314
+
315
+ Which can be used as::
316
+
317
+ // /different/143/emma
318
+ const urls = new URLReverser();
319
+ urls.reverse('different', {'arg1': 143, 'arg2': 'emma'});
320
+
321
+ The default `class_name ` is URLResolver. Reverse should behave exactly as Django's `reverse `.
0 commit comments