Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 238 lines (149 sloc) 11.46 kb
9a34d5c @groue Guides ordering
authored
1 [up](../../../../GRMustache#documentation), [next](compatibility.md)
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
2
e450983 @groue Guides WIP
authored
3 Tag Delegates
4 =============
5
6 - [The GRMustacheTagDelegate protocol](#the-grmustachetagdelegate-protocol)
7 - [Providing Tag Delegates](#providing-tag-delegates)
8 - [Use Cases for Tag Delegates](#use-cases-for-tag-delegates)
9 - [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations)
10
11
12 The GRMustacheTagDelegate protocol
13 ----------------------------------
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
14
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
15 This protocol lets you observe, and possibly alter the rendering of the Mustache tags that consume your data.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
16
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
17 It provides you with a pair of classic "will.../did..." methods that are invoked just before, and just after a tag gets rendered:
26f510c @groue Make clear in guide that variable tag helpers can also be delegates.
authored
18
c1a9d76 @groue Delegate documentation and guide
authored
19 ```objc
20 @protocol GRMustacheTagDelegate<NSObject>
21 @optional
22 - (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object;
b70bb52 @groue Tag Delegate Guide update
authored
23 - (void)mustacheTag:(GRMustacheTag *)tag didRenderObject:(id)object as:(NSString *)rendering;
24 - (void)mustacheTag:(GRMustacheTag *)tag didFailRenderingObject:(id)object withError:(NSError *)error;
60dbce5 @groue Tag Delegates Guide WIP
authored
25 @end
c1a9d76 @groue Delegate documentation and guide
authored
26 ```
7afeec3 @groue Introduce section delegates in the GRMustacheTemplate documentation
authored
27
78dcd72 @groue Tag Delegates Guide WIP
authored
28 - The _object_ argument is the rendered value: a string, a number, an array, depending on the data you provided.
29 - The _tag_ argument represents the rendering tag: `{{ name }}`, `{{# name }}...{{/}}`, etc.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
30
bfd6b3c @groue More links to Reference from Guides
authored
31 See the [GRMustacheTag Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTag.html) for a full documentation of the GRMustacheTag class.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
32
c3c6798 @groue Typo
authored
33 - `mustacheTag:willRenderObject:` returns the value that should be rendered by the tag. It can return its `object` argument, leaving this value untouched, or it can return another value.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
34
78dcd72 @groue Tag Delegates Guide WIP
authored
35 - `mustacheTag:didRenderObject:as:` can let you clean up anything that has been prepared in `mustacheTag:willRenderObject:`. Besides, it is provided with the actual rendering of the tag.
7afeec3 @groue Introduce section delegates in the GRMustacheTemplate documentation
authored
36
88c39cc @groue Typo
authored
37 - `mustacheTag:didFailRenderingObject:withError:` has no other purpose but to let you perform any necessary clean up. There is no error recovery, and the error would eventually come out of the initial rendering method.
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
38
60dbce5 @groue Tag Delegates Guide WIP
authored
39 Note that a tag like `{{ person.name }}` is rendered once. Thus the delegate will be called once. If the person has been found, the rendered object will be the name of the person. If the person could not be found, the rendered object will be `nil`.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
40
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
41 Also: when a section tag `{{# pets }}...{{/ pets }}` is provided with an array, its content is rendered several times. However the delegate will be called once, with the array passed in the _object_ argument.
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
42
43
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
44 Providing Tag Delegates
45 -----------------------
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
46
f7a8085 @groue Typo
authored
47 Tag delegates are an uncommon kind of delegate. There is no object exposing a `delegate` property that you would set to your custom delegate, as one could expect.
1d27df8 @groue Tag Delegate Guide update
authored
48
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
49 Actually, *many* tag delegates can enter the game, observe, and alter the rendering of a template. For example, NSDateFormatter and NSNumberFormatter are tag delegates, and this is how they can format all dates and numbers inside the section they are attached to (see the [NSFormatter Guide](NSFormatter.md)).
50
51 Delegates are scoped. They can observe all tags in a template, or all inner tags of a template section, such as `{{# xxx }}...{{/ xxx }}`.
52
53 You have to ways to inject tag delegates. The simplest is to have them enter the [context stack](runtime.md#the-context-stack). However, sometimes, your delegate should not "pollute" the template rendering with its own keys, and you will need to explicitely derive new contexts. Please follow us:
54
55 ### By Entering the Context Stack
56
57 The [context stack](runtime.md#the-context-stack) contains all objects that can provide values to templates through their methods and properties. It is initialized by the object you render, and it extends by objects attached to sections.
58
59 *An object conforming to the GRMustacheTagDelegate protocol gets "active" as soon as it enters the context stack.*
60
61 For example, consider the following template and rendering code:
62
63 `Document.mustache`
64
65 {{# currentDate }}
66 {{# user }}
67 {{ name }} ({{ age }})
68 {{/ user }}
a4e64b8 @groue more GRMustacheTemplateDelegate documentation
authored
69
c1a9d76 @groue Delegate documentation and guide
authored
70 ```objc
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
71 // Load Document.mustache
f7a8085 @groue Typo
authored
72 GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"Document" bundle:nil error:NULL];
459f57b @groue v4.1.0
authored
73
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
74 // Initialize Document object
75 Document *document = [[Document alloc] init];
76 document.user = self.user;
459f57b @groue v4.1.0
authored
77
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
78 // Render
79 NSString *rendering = [template renderObject:document error:NULL];
80 ```
459f57b @groue v4.1.0
authored
81
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
82 The first object entering the context stack is the document. As long as it conforms to the GRMustacheTagDelegate protocol, it will get notified of the rendering of *all* tags (`{{# currentDate }}`, `{{# user }}...{{/ user }}`, `{{ name }}`, and `{{ age }}`).
c1a9d76 @groue Delegate documentation and guide
authored
83
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
84 As soon as the `{{# user }}...{{/ user }}` section renders, the user enters the context stack. It will get notified of the rendering of the inner tags of the section (explicitly: `{{ name }}`, and `{{ age }}`).
85
86
87 ### By Entering the Base Context of a Template
88
71c17a1 @groue Safe Key Access documentation
authored
89 As soon as an object enters the [context stack](runtime.md#the-context-stack), values returned by the `objectForKeyedSubscript:` and `valueForKey:` methods are available for the templates (see the [Runtime Guide](runtime.md)).
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
90
91 This may be undesirable. You may want an object to be a tag delegate while not providing any value to the templates.
92
93 In this case, you can observe all tags in a template by deriving its *base context*. The base context contains values and tag delegates that are always available for the template rendering. It contains all the ready for use tools of the [standard library](standard_library.md), for example.
94
95 Let's see how `self` can become a tag delegate for the whole template:
c1a9d76 @groue Delegate documentation and guide
authored
96
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
97 ```objc
98 // Load Document.mustache
f7a8085 @groue Typo
authored
99 GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"Document" bundle:nil error:NULL];
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
100
101 // Add self as a tag delegate
277abd7 @groue v6.8.0
authored
102 [template extendBaseContextWithTagDelegate:self];
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
103
104 // Initialize Document object
105 Document *document = [[Document alloc] init];
106 document.user = self.user;
107
108 // Render
109 NSString *rendering = [template renderObject:document error:NULL];
459f57b @groue v4.1.0
authored
110 ```
111
277abd7 @groue v6.8.0
authored
112 See the [GRMustacheTemplate Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTemplate.html) for a full discussion of `extendBaseContextWithTagDelegate:`.
5ce9f34 @groue Tag Delegates Guide WIP
authored
113
114
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
115
116 ### By Deriving a Deep Context
c1a9d76 @groue Delegate documentation and guide
authored
117
277abd7 @groue v6.8.0
authored
118 To illustrate this last use case, let's write an object that renders the uppercase version of all inner tags of the section it is attached to.
4f79605 @groue Document the rendering of lists by variable tags.
authored
119
55d140e @groue Delegate Guide WIP
authored
120 We do not want it to "pollute" the [context stack](runtime.md#the-context-stack), because we want it to be able to transform *all* tags, including `{{ description }}`. Should our object be in the context stack, its own description (inherited from NSObject) would render, and we would have a bug.
459f57b @groue v4.1.0
authored
121
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
122 `Document.mustache`
16f24a3 @groue more GRMustacheTemplateDelegate documentation
authored
123
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
124 {{ firstName }} {{ lastName }}: {{ description }}
125 {{# uppercase }}
126 {{ firstName }} {{ lastName }}: {{ description }}
127 {{/ uppercase }}
79d2814 @groue GRMustacheTemplateDelegate documentation
authored
128
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
129 We expect the rendering to be:
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
130
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
131 John Locke: English philosopher and physician
132 JOHN LOCKE: ENGLISH PHILOSOPHER AND PHYSICIAN
133
134 Here is the implementation of our UppercaseTagDelegate class.
135
136 It conforms to GRMustacheTagDelegate, obviously, but also to the [GRMustacheRendering protocol](rendering_objects.md). This protocol allows it to avoid the default rendering, that would send it right into the the context stack.
84e5afe @groue Delegate Guide WIP
authored
137
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
138 ```objc
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
139 @interface UppercaseTagDelegate : NSObject<GRMustacheTagDelegate, GRMustacheRendering>
c1a9d76 @groue Delegate documentation and guide
authored
140 @end
141
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
142 @implementation UppercaseTagDelegate
143
144 // GRMustacheRendering
d467548 @groue Remove vestigial __autoreleasing ownership qualifiers on NSError **
authored
145 - (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
146 {
147 // Render the Mustache tag with an extended context
148 context = [context contextByAddingTagDelegate:self];
149 return [tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
150 }
c1a9d76 @groue Delegate documentation and guide
authored
151
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
152 // GRMustacheTagDelegate
153 - (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
154 {
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
155 return [[object description] uppercaseString];
c1a9d76 @groue Delegate documentation and guide
authored
156 }
157
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
158 @end
159 ```
160
277abd7 @groue v6.8.0
authored
161 See the [GRMustacheContext Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheContext.html) for a full discussion of `contextByAddingTagDelegate:`.
162
163 See also the [GRMustacheRendering Protocol Reference](http://groue.github.io/GRMustache/Reference/Protocols/GRMustacheRendering.html) and [GRMustacheTag Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTag.html) for a full documentation of GRMustacheRendering and GRMustacheTag.
7baa1db @groue More links from Guides to Reference
authored
164
165
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
166 Use Cases for Tag Delegates
167 ---------------------------
168
f06d0be @groue Better documentation for providing default values
authored
169 ### Default Values
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
170
171 ```objc
c1a9d76 @groue Delegate documentation and guide
authored
172 - (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object
173 {
174 if (object == nil) {
175 NSLog(@"Missing value for %@", tag);
176 return @"DEFAULT";
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
177 }
c1a9d76 @groue Delegate documentation and guide
authored
178 return object;
86003ca @groue more GRMustacheTemplateDelegate documentation
authored
179 }
71638db @groue v1.12.1
authored
180 ```
181
5ce9f34 @groue Tag Delegates Guide WIP
authored
182 Your application log will contain lines like:
183
184 Missing value for <GRMustacheVariableTag `{{ name }}` at line 3>
185 Missing value for <GRMustacheVariableTag `{{ fullDateFormat(joinDate) }}` at line 12>
186
fda93a5 @groue Update helpers and delegate documentation so that they better fit with f...
authored
187
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
188 ### Cross-Platform Filters
ee5f05d @groue Guide: "Tag Delegates as Cross-Platform Filters"
authored
189
84e5afe @groue Delegate Guide WIP
authored
190 Tag delegates can alter the rendering of all tags inside the section they are attached to.
bd53b5f @groue Obsolete number formatting and localization sample code
authored
191
192 Let's consider the behavior of NSFormatter in GRMustache. They are able to format all variable tags inside a section (check the [NSFormatter Guide](NSFormatter.md)).
ee5f05d @groue Guide: "Tag Delegates as Cross-Platform Filters"
authored
193
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
194 For example, `{{#percent}}x = {{x}}{{/percent}}` renders as `x = 50 %` when `percent` is attached to an NSNumberFormatter. That is because formatters are tag delegates, just as our UppercaseTagDelegate class above.
bd53b5f @groue Obsolete number formatting and localization sample code
authored
195
196 We could also use [filters](filters.md) in order to format numbers: `x = {{ percent(x) }}` would render just as well.
ee5f05d @groue Guide: "Tag Delegates as Cross-Platform Filters"
authored
197
198 However, `{{#percent}}x = {{x}}{{/percent}}` has one advantage over `x = {{ percent(x) }}`: it uses plain Mustache syntax, and is compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations).
199
200 With such a common template, it's now a matter of providing different data, depending on the platform:
201
202 // common template
203 {{#percent}}x = {{x}}{{/percent}}
204
205 // data for GRMustache
206 {
207 "x": 0.5,
bd53b5f @groue Obsolete number formatting and localization sample code
authored
208 "percent": (some well-configured NSNumberFormatter)
ee5f05d @groue Guide: "Tag Delegates as Cross-Platform Filters"
authored
209 }
210
211 // data for other Mustache implementations
212 {
213 "percent": {
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
214 "x": "50 %" (computed during the "ViewModel preparation phase")
ee5f05d @groue Guide: "Tag Delegates as Cross-Platform Filters"
authored
215 }
216 }
217
acba3af @groue Delegate Guide conformance to ViewModel Guide
authored
218 See? When you, GRMustache user, can provide your raw model data and have tag delegates do the formatting, users of the other implementations can still *prepare* their data and build a "ViewModel" that contains the values that should be rendered. Eventually both renderings are identical.
c92665d @groue "Tag Delegates as Cross-Platform Filters" cleanup
authored
219
bd53b5f @groue Obsolete number formatting and localization sample code
authored
220
fe9e7cf @groue Delegate Guide WIP
authored
221 ### Funny Hooks
222
223 Many objects of the [standard library](standard_library.md) are tag delegates. They are all built on top of public APIs, APIs that you can use, so check them out. For example:
224
0b8c470 @groue Update direct links to sources
authored
225 - GRMustacheHTMLEscapeFilter is quite simple: [GRMustacheHTMLLibrary.m](../src/classes/Services/StandardLibrary/GRMustacheHTMLLibrary.m)
226 - GRMustacheLocalizer is less simple: [GRMustacheLocalizer.m](../src/classes/Services/StandardLibrary/GRMustacheLocalizer.m)
fe9e7cf @groue Delegate Guide WIP
authored
227
228
c1a9d76 @groue Delegate documentation and guide
authored
229 Compatibility with other Mustache implementations
230 -------------------------------------------------
fda93a5 @groue Update helpers and delegate documentation so that they better fit with f...
authored
231
c1a9d76 @groue Delegate documentation and guide
authored
232 The [Mustache specification](https://github.com/mustache/spec) does not have the concept of "tag delegates".
fda93a5 @groue Update helpers and delegate documentation so that they better fit with f...
authored
233
b9e3ed7 @groue wording
authored
234 **As a consequence, if your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), use `GRMustacheTagDelegate` with great care.**
fda93a5 @groue Update helpers and delegate documentation so that they better fit with f...
authored
235
236
9a34d5c @groue Guides ordering
authored
237 [up](../../../../GRMustache#documentation), [next](compatibility.md)
Something went wrong with that request. Please try again.