-
Notifications
You must be signed in to change notification settings - Fork 16
[mustache_template] Add example app #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
eeb1b88
97bb613
4f8e4ac
98925c5
2bb2389
5633d58
3924d65
cc69780
56a54b8
4102b90
534b7ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,4 +59,4 @@ gradlew.bat | |
|
|
||
| .project | ||
| .classpath | ||
| .settings | ||
| .settings | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| <?code-excerpt path-base="example/lib"?> | ||
|
|
||
| # Mustache templates | ||
|
|
||
| A Dart library to parse and render [mustache templates](https://mustache.github.io/). | ||
|
|
@@ -7,34 +9,32 @@ See the [mustache manual](https://mustache.github.io/mustache.5.html) for detail | |
| This library passes all [mustache specification](https://github.com/mustache/spec/tree/master/specs) tests. | ||
|
|
||
| ## Example usage | ||
| ```dart | ||
| import 'package:mustache_template/mustache_template.dart'; | ||
|
|
||
| main() { | ||
| var source = ''' | ||
| {{# names }} | ||
| <?code-excerpt "readme_excerpts.dart (example_usage)"?> | ||
| ```dart | ||
| var source = ''' | ||
| {{# names }} | ||
| <div>{{ lastname }}, {{ firstname }}</div> | ||
| {{/ names }} | ||
| {{^ names }} | ||
| <div>No names.</div> | ||
| {{/ names }} | ||
| {{! I am a comment. }} | ||
| '''; | ||
|
|
||
| var template = Template(source, name: 'template-filename.html'); | ||
|
|
||
| var output = template.renderString({'names': [ | ||
| {'firstname': 'Greg', 'lastname': 'Lowe'}, | ||
| {'firstname': 'Bob', 'lastname': 'Johnson'} | ||
| ]}); | ||
|
|
||
| print(output); | ||
| } | ||
| {{/ names }} | ||
| {{^ names }} | ||
| <div>No names.</div> | ||
| {{/ names }} | ||
| {{! I am a comment. }} | ||
| '''; | ||
|
|
||
| var template = Template(source, name: 'template-filename.html'); | ||
|
|
||
| String output = template.renderString(<String, dynamic>{ | ||
| 'names': <Map<String, String>>[ | ||
| <String, String>{'firstname': 'Greg', 'lastname': 'Lowe'}, | ||
| <String, String>{'firstname': 'Bob', 'lastname': 'Johnson'} | ||
| ] | ||
| }); | ||
| ``` | ||
|
|
||
| A template is parsed when it is created, after parsing it can be rendered any number of times with different values. A TemplateException is thrown if there is a problem parsing or rendering the template. | ||
|
|
||
| The Template contstructor allows passing a name, this name will be used in error messages. When working with a number of templates, it is important to pass a name so that the error messages specify which template caused the error. | ||
| The Template constructor allows passing a name, this name will be used in error messages. When working with a number of templates, it is important to pass a name so that the error messages specify which template caused the error. | ||
|
|
||
| By default all output from `{{variable}}` tags is html escaped, this behaviour can be changed by passing htmlEscapeValues : false to the Template constructor. You can also use a `{{{triple mustache}}}` tag, or a unescaped variable tag `{{&unescaped}}`, the output from these tags is not escaped. | ||
|
|
||
|
|
@@ -53,65 +53,56 @@ By default all output from `{{variable}}` tags is html escaped, this behaviour c | |
|
|
||
| ## Nested paths | ||
|
|
||
| <?code-excerpt "readme_excerpts.dart (nested_paths)"?> | ||
| ```dart | ||
| var t = Template('{{ author.name }}'); | ||
| var output = template.renderString({'author': {'name': 'Greg Lowe'}}); | ||
| var template = Template('{{ author.name }}'); | ||
| String output = template.renderString(<String, dynamic>{ | ||
| 'author': <String, String>{'name': 'Greg Lowe'} | ||
| }); | ||
| ``` | ||
|
|
||
| ## Partials - example usage | ||
|
|
||
| <?code-excerpt "readme_excerpts.dart (partials)"?> | ||
| ```dart | ||
| var partial = Template('{{ foo }}', name: 'partial'); | ||
|
|
||
| var partial = Template('{{ foo }}', name: 'partial'); | ||
|
|
||
| var resolver = (String name) { | ||
| if (name == 'partial-name') { // Name of partial tag. | ||
| return partial; | ||
| } | ||
| }; | ||
|
|
||
| var t = Template('{{> partial-name }}', partialResolver: resolver); | ||
| Template? resolver(String name) { | ||
| if (name == 'partial-name') { | ||
| // Name of partial tag. | ||
| return partial; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| var output = t.renderString({'foo': 'bar'}); // bar | ||
| var t = Template('{{> partial-name }}', partialResolver: resolver); | ||
|
|
||
| String output = t.renderString(<String, dynamic>{'foo': 'bar'}); | ||
| ``` | ||
|
|
||
| ## Lambdas - example usage | ||
|
|
||
| <?code-excerpt "readme_excerpts.dart (lambdas)"?> | ||
| ```dart | ||
| var t = Template('{{# foo }}'); | ||
| var lambda = (_) => 'bar'; | ||
| t.renderString({'foo': lambda}); // bar | ||
| ``` | ||
|
|
||
| ```dart | ||
| var t = Template('{{# foo }}hidden{{/ foo }}'); | ||
| var lambda = (_) => 'shown'; | ||
| t.renderString('foo': lambda); // shown | ||
| ``` | ||
|
|
||
| ```dart | ||
| var t = Template('{{# foo }}oi{{/ foo }}'); | ||
| var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| t.renderString({'foo': lambda}); // <b>OI</b> | ||
| ``` | ||
|
|
||
| ```dart | ||
| var t = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b> | ||
| ``` | ||
|
|
||
| ```dart | ||
| var t = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b> | ||
| // Simple lambda | ||
| var t1 = Template('{{# foo }}inner{{/ foo }}'); | ||
| Object lambda1(Object? _) => 'bar'; | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You've removed the Adopting code excepts should not remove useful parts of examples. |
||
| // Lambda returning text for a hidden section | ||
| var t2 = Template('{{# foo }}hidden{{/ foo }}'); | ||
| Object lambda2(Object? _) => 'shown'; | ||
|
|
||
| // Lambda Context | ||
| var t3 = Template('{{# foo }}oi{{/ foo }}'); | ||
| Object lambda3(LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
|
|
||
| // Lambda Context with variables | ||
| var t4 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda4(LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
|
|
||
| // Lambda Context re-parsing source | ||
| var t5 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda5(LambdaContext ctx) => ctx.renderSource('${ctx.source} {{cmd}}'); | ||
| ``` | ||
|
|
||
| In the following example `LambdaContext.renderSource(source)` re-parses the source string in the current context, this is the default behaviour in many mustache implementations. Since re-parsing the content is slow, and often not required, this library makes this step optional. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was the structure of the README changed here? |
||
|
|
||
| ```dart | ||
| var t = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| var lambda = (LambdaContext ctx) => ctx.renderSource(ctx.source + ' {{cmd}}'); | ||
| t.renderString({'foo': lambda, 'bar': 'pub', 'cmd': 'build'}); // pub build | ||
| ``` | ||
| In the last lambda example `LambdaContext.renderSource(source)` re-parses the source string in the current context, this is the default behaviour in many mustache implementations. Since re-parsing the content is slow, and often not required, this library makes this step optional. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| // Copyright 2026 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| // ignore_for_file: avoid_print | ||
|
|
||
| import 'package:mustache_template/mustache_template.dart'; | ||
|
|
||
| /// The main entrypoint for the example app. | ||
| void main() { | ||
| exampleUsage(); | ||
| nestedPaths(); | ||
| partialsExample(); | ||
| lambdasExample(); | ||
| } | ||
|
|
||
| /// Demonstrates basic usage of mustache templates. | ||
| void exampleUsage() { | ||
| const source = ''' | ||
| {{# names }} | ||
| <div>{{ lastname }}, {{ firstname }}</div> | ||
| {{/ names }} | ||
| {{^ names }} | ||
| <div>No names.</div> | ||
| {{/ names }} | ||
| {{! I am a comment. }} | ||
| '''; | ||
|
|
||
| final template = Template(source, name: 'template-filename.html'); | ||
|
|
||
| final String output = template.renderString(<String, dynamic>{ | ||
| 'names': <Map<String, String>>[ | ||
| <String, String>{'firstname': 'Greg', 'lastname': 'Lowe'}, | ||
| <String, String>{'firstname': 'Bob', 'lastname': 'Johnson'}, | ||
| ], | ||
| }); | ||
|
|
||
| print(output); | ||
| } | ||
|
|
||
| /// Demonstrates how to access nested map properties. | ||
| void nestedPaths() { | ||
| final template = Template('The author is {{ author.name }}'); | ||
| final String output = template.renderString(<String, dynamic>{ | ||
| 'author': <String, String>{'name': 'Greg Lowe'}, | ||
| }); | ||
| print(output); | ||
| } | ||
|
|
||
| /// Demonstrates the usage of partials with a custom resolver. | ||
| void partialsExample() { | ||
| final partial = Template('{{ foo }}', name: 'partial'); | ||
|
|
||
| Template? resolver(String name) { | ||
| if (name == 'partial-name') { | ||
| // Name of partial tag. | ||
| return partial; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| final t = Template('{{> partial-name }}', partialResolver: resolver); | ||
|
|
||
| final String output = t.renderString(<String, dynamic>{'foo': 'bar'}); | ||
| print(output); // bar | ||
| } | ||
|
|
||
| /// Demonstrates various usages of lambdas, including hidden sections and lambda contexts. | ||
| void lambdasExample() { | ||
| // Simple lambda | ||
| final t1 = Template('{{# foo }}inner{{/ foo }}'); | ||
| Object lambda1(Object? _) => 'bar'; | ||
| print(t1.renderString(<String, dynamic>{'foo': lambda1})); // bar | ||
|
|
||
| // Lambda returning text for a hidden section | ||
| final t2 = Template('{{# foo }}hidden{{/ foo }}'); | ||
| Object lambda2(Object? _) => 'shown'; | ||
| print(t2.renderString(<String, dynamic>{'foo': lambda2})); // shown | ||
|
|
||
| // Lambda Context | ||
| final t3 = Template('{{# foo }}oi{{/ foo }}'); | ||
| Object lambda3(LambdaContext ctx) => | ||
| '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| print(t3.renderString(<String, dynamic>{'foo': lambda3})); // <b>OI</b> | ||
|
|
||
| // Lambda Context with variables | ||
| final t4 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda4(LambdaContext ctx) => | ||
| '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| print(t4.renderString( | ||
| <String, dynamic>{'foo': lambda4, 'bar': 'pub'})); // <b>PUB</b> | ||
|
|
||
| // Lambda Context re-parsing source | ||
| final t5 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda5(LambdaContext ctx) => | ||
| ctx.renderSource('${ctx.source} {{cmd}}'); | ||
| print(t5.renderString(<String, dynamic>{ | ||
| 'foo': lambda5, | ||
| 'bar': 'pub', | ||
| 'cmd': 'build', | ||
| })); // pub build | ||
| } |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of having this file? All of the code appears to exactly duplicate code in main.dart. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // Copyright 2026 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| // This file exists solely to host compiled excerpts for README.md, and is not | ||
| // intended for use as an actual example application. | ||
|
|
||
| // ignore_for_file: avoid_print | ||
| // ignore_for_file: omit_local_variable_types | ||
| // ignore_for_file: strict_raw_type | ||
| // ignore_for_file: prefer_final_locals | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why have you disabled core style rules? We have a consistent style for a reason. |
||
|
|
||
| import 'package:mustache_template/mustache_template.dart'; | ||
|
|
||
| /// Example for basic usage of a mustache template. | ||
| void exampleUsageSnippet() { | ||
| // #docregion example_usage | ||
| var source = ''' | ||
| {{# names }} | ||
| <div>{{ lastname }}, {{ firstname }}</div> | ||
| {{/ names }} | ||
| {{^ names }} | ||
| <div>No names.</div> | ||
| {{/ names }} | ||
| {{! I am a comment. }} | ||
| '''; | ||
|
|
||
| var template = Template(source, name: 'template-filename.html'); | ||
|
|
||
| String output = template.renderString(<String, dynamic>{ | ||
| 'names': <Map<String, String>>[ | ||
| <String, String>{'firstname': 'Greg', 'lastname': 'Lowe'}, | ||
| <String, String>{'firstname': 'Bob', 'lastname': 'Johnson'} | ||
| ] | ||
| }); | ||
| // #enddocregion example_usage | ||
|
|
||
| print(output); | ||
| } | ||
|
|
||
| /// Example for rendering nested paths in a template. | ||
| void nestedPathsSnippet() { | ||
| // #docregion nested_paths | ||
| var template = Template('{{ author.name }}'); | ||
| String output = template.renderString(<String, dynamic>{ | ||
| 'author': <String, String>{'name': 'Greg Lowe'} | ||
| }); | ||
| // #enddocregion nested_paths | ||
| print(output); | ||
| } | ||
|
|
||
| /// Example for using partials. | ||
| void partialsSnippet() { | ||
| // #docregion partials | ||
| var partial = Template('{{ foo }}', name: 'partial'); | ||
|
|
||
| Template? resolver(String name) { | ||
| if (name == 'partial-name') { | ||
| // Name of partial tag. | ||
| return partial; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| var t = Template('{{> partial-name }}', partialResolver: resolver); | ||
|
|
||
| String output = t.renderString(<String, dynamic>{'foo': 'bar'}); | ||
| // #enddocregion partials | ||
| print(output); // bar | ||
| } | ||
|
|
||
| /// Example for using lambdas in a template. | ||
| void lambdasSnippet() { | ||
| // #docregion lambdas | ||
| // Simple lambda | ||
| var t1 = Template('{{# foo }}inner{{/ foo }}'); | ||
| Object lambda1(Object? _) => 'bar'; | ||
| // #enddocregion lambdas | ||
| print(t1.renderString(<String, dynamic>{'foo': lambda1})); // bar | ||
|
|
||
| // #docregion lambdas | ||
| // Lambda returning text for a hidden section | ||
| var t2 = Template('{{# foo }}hidden{{/ foo }}'); | ||
| Object lambda2(Object? _) => 'shown'; | ||
| // #enddocregion lambdas | ||
| print(t2.renderString(<String, dynamic>{'foo': lambda2})); // shown | ||
|
|
||
| // #docregion lambdas | ||
| // Lambda Context | ||
| var t3 = Template('{{# foo }}oi{{/ foo }}'); | ||
| Object lambda3(LambdaContext ctx) => | ||
| '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| // #enddocregion lambdas | ||
| print(t3.renderString(<String, dynamic>{'foo': lambda3})); // <b>OI</b> | ||
|
|
||
| // #docregion lambdas | ||
| // Lambda Context with variables | ||
| var t4 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda4(LambdaContext ctx) => | ||
| '<b>${ctx.renderString().toUpperCase()}</b>'; | ||
| // #enddocregion lambdas | ||
| print(t4.renderString( | ||
| <String, dynamic>{'foo': lambda4, 'bar': 'pub'})); // <b>PUB</b> | ||
|
|
||
| // #docregion lambdas | ||
| // Lambda Context re-parsing source | ||
| var t5 = Template('{{# foo }}{{bar}}{{/ foo }}'); | ||
| Object lambda5(LambdaContext ctx) => | ||
| ctx.renderSource('${ctx.source} {{cmd}}'); | ||
| // #enddocregion lambdas | ||
| print(t5.renderString(<String, dynamic>{ | ||
| 'foo': lambda5, | ||
| 'bar': 'pub', | ||
| 'cmd': 'build' | ||
| })); // pub build | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert the change to this file.