- Overview
- Defining your own filters
- Variadic filters
- Filters that return rendering objects
- Filters namespaces
- Filters errors
- Sample code
- Compatibility with other Mustache implementations
You apply a filter just like calling a function, with parentheses:
-
My name is {{ uppercase(name) }}
would renderMy name is ARTHUR
, provided with "Arthur" as a name. -
Filters can chain:
{{ uppercase(reversed(name)) }}
would renderRUHTRA
. -
Filters can apply to compound key paths:
{{ uppercase(person.name) }}
. -
You can extract values from filtered values:
{{ last(people).name }}
. -
You can filter sections as well :
{{^ isEmpty(people) }}...{{/ isEmpty(people) }}
renders if the people collection is not empty.For brevity's sake, closing section tags can be empty:
{{^ isEmpty(people) }}...{{/}}
is valid. -
Filters can take several arguments:
{{ localize(date, format) }}
. -
Filters can return filters:
{{ f(x)(y) }}
.
You can implement your own filters with objects that conform to the GRMustacheFilter
protocol.
This protocol defines a single required method:
@protocol GRMustacheFilter <NSObject>
@required
- (id)transformedValue:(id)object;
@end
You can for instance declare a filter that outputs numbers as percentages:
@interface PercentFilter : NSObject<GRMustacheFilter>
@end
@implementation PercentFilter
- (id)transformedValue:(id)object
{
NSNumberFormatter *percentNumberFormatter = [[NSNumberFormatter alloc] init];
percentNumberFormatter.numberStyle = kCFNumberFormatterPercentStyle;
return [numberFormatter stringFromNumber:object];
}
@end
id percentFilters = [[PercentFilter alloc] init];
The protocol comes with a GRMustacheFilter
class, which provides a convenient method for building a filter without implementing a full class that conforms to the protocol:
id data = @{
@"gain": @0.5,
@"percent": [GRMustacheFilter filterWithBlock:^id(id object) {
NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
numberFormatter.numberStyle = kCFNumberFormatterPercentStyle;
return [numberFormatter stringFromNumber:object];
}],
};
// Enjoy your 50% productivity bump!
NSString *templateString = @"Enjoy your {{ percent(gain) }} productivity bump!";
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
A variadic filter is a filter that accepts a variable number of arguments.
You create a variadic filter with the variadicFilterWithBlock:
method:
Document.mustache
:
{{#object1}}
{{ dateFormat(date, format) }}
{{/object1}}
{{#object2}}
{{ dateFormat(date, format) }}
{{/object2}}
Render.m
:
id data = @{
@"object1": @{
@"format": @"yyyy-MM-dd 'at' HH:mm",
@"date": [NSDate date]
},
@"object2": @{
@"format": @"yyyy-MM-dd",
@"date": [NSDate date]
},
@"dateFormat": [GRMustacheFilter variadicFilterWithBlock:^id(NSArray *arguments) {
// first argument is date
NSDate *date = [arguments objectAtIndex:0];
// second argument is format
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = [arguments objectAtIndex:1];
// compute the result
return [dateFormatter stringFromDate:date];
}]
};
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
2012-10-28 at 17:10
2012-10-28
"Rendering objects" are objects that perform a custom rendering. They are described in detail in the Rendering Objects Guide.
A fundamental technique of advanced GRMustache rendering is filters that return rendering objects. For example:
I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.
would render, depending on the number of cats:
I have 1 cat.
I have 5 cats.
The pluralize
filter returns an object that is able to pluralize the inner content of the section it is attached to. Go check the Rendering Objects Guide for more details.
Just as you can provide an object hierarchy for rendered values, and extract person.pet.name
from it, you can provide filters as an object hierarchy, and "namespace" your filters. For instance, let's declare the math.abs
filter, and render {{ math.abs(x) }}
:
NSString *templateString = @"{{ math.abs(x) }}";
GRMustacheTemplate *template = [GRMustacheTemplate templateFromString:templateString error:NULL];
id data = @{
@"x": @(-1),
@"math": @{
@"abs": [GRMustacheFilter filterWithBlock:^id(id object) {
return @(abs([object intValue]));
}],
},
};
// 1
NSString *rendering = [template renderObject:data error:NULL];
Should a filter be missing, or should the matching object not conform to the GRMustacheFilter
protocol, GRMustache will return an error of domain GRMustacheErrorDomain
and code GRMustacheErrorCodeRenderingError
.
The message describes the exact place where the error occur has occurred:
Missing filter for key `f` in tag `{{ f(foo) }}` at line 13 of /path/to/template.
Object for key `f` in tag `{{ f(foo) }}` at line 13 of /path/to/template does not conform to GRMustacheFilter protocol: "blah"
Custom filters are used in many items of the standard library. NSFormatter are ready-made filters in GRMustache. Go check inspiration there.
The Mustache specification does not have any concept of "filters".
If your goal is to design templates that are compatible with other Mustache implementations, do NOT use filters.
Instead, have a look at tag delegates, especially the Cross-Platform Filters section of the Tag Delegates Guide.