Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 191 lines (145 sloc) 6.597 kB
438ce09 @groue indexes.md guide
authored
1 [up](../sample_code.md), [next](../forking.md)
2
3 Indexes
4 =======
5
6 Mustache is a simple template language. Its [specification](https://github.com/mustache/spec) does not provide any built-in access to loop indexes. It does not provide any way to render a section at the beginning of the loop, and another section at the end. It does not help you render different sections for odd and even indexes.
7
8 If your goal is to design your templates so that they are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), the best way to render indices and provide custom looping logic is to have each of your data objects provide with its index, regardless of how tedious it may be for you to prepare the rendered data.
9
10 For instance, instead of `[ { name:'Alice' }, { name:'Bob' } ]`, you would provide: `[ { name:'Alice', index:0, first:true, last:false, even:false }, { name:'Bob', index:1, first:false, last:true, even:true } ]`.
11
12 GRMustache solution: proxy objects
13 ----------------------------------
14
19f0f57 @groue Link from guide to sample project
authored
15 **[Download the code](../../../../tree/master/Guides/sample_code/indexes)**
3f50ab0 @groue Links to sample code projects hosted at https://github.com/groue/GRMu…
authored
16
f64c20e @groue indexes.md guide
authored
17 The [GRMustacheTemplateDelegate](../delegate.md) protocol can help you extend the mustache language, and avoid preparing your data.
438ce09 @groue indexes.md guide
authored
18
f64c20e @groue indexes.md guide
authored
19 Below we'll implement the special keys `index`, `first`, and `even`. We'll render the following template:
438ce09 @groue indexes.md guide
authored
20
21 <ul>
22 {{#people}}
23 <li class="{{#even}}even{{/even}} {{#first}}first{{/first}}">
24 {{index}}:{{name}}
25 </li>
26 {{/people}}
27 </ul>
28
29 We expect, on output, the following rendering:
30
31 <ul>
32 <li class="even first">
33 0: Alice
34 </li>
35 <li class="">
36 1: Bob
37 </li>
38 <li class="even">
39 2: Craig
40 </li>
41 </ul>
42
43 Here is the rendering code:
44
45 ```objc
46 @implementation Document
47
48 - (NSString *)render
49 {
50 /**
35162b7 @groue Hard wrap documentation to 80 columns
authored
51 * First, let's attach an array of people to the `people` key, so that they
52 * are sequentially rendered by the `{{#people}}...{{/people}}` sections.
53 *
54 * We'll use a NSDictionary for storing the data, but you can use any other
55 * KVC-compliant container.
438ce09 @groue indexes.md guide
authored
56 */
57
58 Person *alice = [Person personWithName:@"Alice"];
59 Person *bob = [Person personWithName:@"Bob"];
60 Person *craig = [Person personWithName:@"Craig"];
61 NSArray *people = [NSArray arrayWithObjects: alice, bob, craig, nil];
62 NSDictionary *data = [NSDictionary dictionaryWithObject:people forKey:@"people"];
63
64 /**
65 Render. The rendering of indices will happen in the
66 GRMustacheTemplateDelegate methods, hereafter.
67 */
68
69 GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"template" bundle:nil error:NULL];
70 template.delegate = self;
71 return [template renderObject:data];
72 }
73 ```
74
75 The people will provide the `name` key needed by the template. But we haven't told yet how the `index`, `first` and `even` keys will be implemented.
76
f64c20e @groue indexes.md guide
authored
77 Here is the trick: we'll actually intercept arrays before they are rendered by GRMustache. We'll replace them with arrays of proxy objects which will forward to the original elements all keys, such as `name`, but the `index`, `first` and `even` keys.
438ce09 @groue indexes.md guide
authored
78
79 Let's first declare our proxy class, we'll implement it later:
80
81 ```objc
82 @interface ArrayElementProxy : NSObject
83 - (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array;
84 @end
85 ```
86
f64c20e @groue indexes.md guide
authored
87 We don't need to declare more: this is enough for us to create proxies and make sure they have all the information they need to perform their job.
88
89 Now let's replace array elements with proxies before they are rendered:
438ce09 @groue indexes.md guide
authored
90
91 ```objc
92 @interface Document() <GRMustacheTemplateDelegate>
93 @end
94
95 @implementation Document()
96
97 /**
35162b7 @groue Hard wrap documentation to 80 columns
authored
98 * This method is called when the template is about to render a tag.
438ce09 @groue indexes.md guide
authored
99 */
459f57b @groue v4.1.0
authored
100 - (void)template:(GRMustacheTemplate *)template willInterpretReturnValueOfInvocation:(GRMustacheInvocation *)invocation as:(GRMustacheInterpretation)interpretation
438ce09 @groue indexes.md guide
authored
101 {
102 /**
35162b7 @groue Hard wrap documentation to 80 columns
authored
103 * The invocation object tells us which object is about to be rendered.
438ce09 @groue indexes.md guide
authored
104 */
105
106 if ([invocation.returnValue isKindOfClass:[NSArray class]]) {
107
108 /**
35162b7 @groue Hard wrap documentation to 80 columns
authored
109 * If it is an NSArray, create a new array containing proxies.
438ce09 @groue indexes.md guide
authored
110 */
111
112 NSArray *array = invocation.returnValue;
1c5a11d @groue indexes.md guide
authored
113 NSMutableArray *proxiesArray = [NSMutableArray arrayWithCapacity:array.count];
438ce09 @groue indexes.md guide
authored
114 [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
1c5a11d @groue indexes.md guide
authored
115 ArrayElementProxy *proxy = [[ArrayElementProxy alloc] initWithObjectAtIndex:idx inArray:array];
116 [proxiesArray addObject:proxy];
438ce09 @groue indexes.md guide
authored
117 }];
118
119 /**
35162b7 @groue Hard wrap documentation to 80 columns
authored
120 * Now set the invocation's returnValue to the array of proxies: it will
121 * be rendered instead.
438ce09 @groue indexes.md guide
authored
122 */
123
1c5a11d @groue indexes.md guide
authored
124 invocation.returnValue = proxiesArray;
438ce09 @groue indexes.md guide
authored
125 }
126 }
127
128 @end
129 ```
130
131 We're soon done.
132
1c5a11d @groue indexes.md guide
authored
133 The implementation of ArrayElementProxy is straightforward, as long as one remembers that GRMustache fetches values with the `valueForKey:` method, and renders values returned by the `description` method. See [Guides/runtime.md](../runtime.md) for more information.
438ce09 @groue indexes.md guide
authored
134
135 ```objc
136 @interface ArrayElementProxy()
137 @property (nonatomic) NSUInteger index;
138 @property (nonatomic, strong) NSArray *array;
139 @end
140
141 @implementation ArrayElementProxy
142 @synthesize index=_index;
143 @synthesize array=_array;
144
145 - (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array
146 {
147 self = [super init];
148 if (self) {
149 self.index = index;
150 self.array = array;
151 }
152 return self;
153 }
154
1c5a11d @groue indexes.md guide
authored
155 // Support for `{{.}}`, not used in our sample template, but a honest proxy
156 // should implement it.
438ce09 @groue indexes.md guide
authored
157 - (NSString *)description
158 {
159 id originalObject = [self.array objectAtIndex:self.index];
160 return [originalObject description];
161 }
162
163 - (id)valueForKey:(NSString *)key
164 {
165 // support for `{{index}}`
166 if ([key isEqualToString:@"index"]) {
167 return [NSNumber numberWithUnsignedInteger:self.index];
168 }
169
170 // support for `{{#first}}` and `{{^first}}`
171 if ([key isEqualToString:@"first"]) {
172 return [NSNumber numberWithBool:(self.index == 0)];
173 }
174
175 // support for `{{#even}}` and `{{^even}}`
176 if ([key isEqualToString:@"even"]) {
177 return [NSNumber numberWithBool:((self.index % 2) == 0)];
178 }
179
180 // for all other keys, forward to original array element
181 id originalObject = [self.array objectAtIndex:self.index];
182 return [originalObject valueForKey:key];
183 }
184
185 @end
186 ```
187
c1aaf13 @groue More links from guides to sample projects
authored
188 **[Download the code](../../../../tree/master/Guides/sample_code/indexes)**
189
438ce09 @groue indexes.md guide
authored
190 [up](../sample_code.md), [next](../forking.md)
Something went wrong with that request. Please try again.