Add support for index in each tags

It was requested that the current index could be accessed in the each
tag.
This commit is contained in:
Travis Parks 2013-07-20 12:06:38 -04:00
parent 3795a77354
commit d7a0ab3b38
16 changed files with 92 additions and 16 deletions

View File

@ -86,6 +86,16 @@ If you need to print out a block of text for each item in a collection, use the
{{/each}}
Within the context of the **each** block, the scope changes to the current item. So, in the example above, `Name` would refer to a property in the `Customer` class.
Additionally, you can access the current index into the collection being enumerated using the **index** tag.
<ul>
{{#each Items}}
<li class="list-item{{#index}}" value="{{Value}}">{{Description}}</li>
{{/each}}
</ul>
This will build an HTML list, building a list of items with `Description` and `Value` properties. Additionally, the `index` tag is used to create a CSS class with increasing numbers.
## The 'with' tag
Within a block of text, you may refer to a same top-level placeholder over and over. You can cut down the amount of text by using the **with** tag.

View File

@ -990,6 +990,20 @@ Last";
Assert.AreEqual("Before123After", result, "The wrong text was generated.");
}
/// <summary>
/// We can use the index tag to get the current iteration.
/// </summary>
[TestMethod]
public void TestCompile_Each_Index_PrintsIndexOfItem()
{
FormatCompiler parser = new FormatCompiler();
const string format = "<ul>{{#each this}}<li value=\"{{this}}\">Item {{#index}}</li>{{/each}}</ul>";
Generator generator = parser.Compile(format);
string result = generator.Render(new int[] { 1, 2, 3 });
const string expected = @"<ul><li value=""1"">Item 0</li><li value=""2"">Item 1</li><li value=""3"">Item 2</li></ul>";
Assert.AreEqual(expected, result, "The wrong text was generated.");
}
#endregion
#region With
@ -1038,7 +1052,7 @@ Last";
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
}
public override void GetText(TextWriter writer, Dictionary<string, object> arguments)
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
{
writer.Write(arguments["param"]);
}

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyVersion("0.1.2.0")]
[assembly: AssemblyFileVersion("0.1.2.0")]

View File

@ -72,7 +72,7 @@ namespace Mustache
}
}
void IGenerator.GetText(KeyScope scope, TextWriter writer)
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
{
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
@ -93,7 +93,7 @@ namespace Mustache
{
foreach (IGenerator generator in generators)
{
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer);
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer, context.Data);
if (context.WriterNeedsConsidated)
{
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));

View File

@ -54,9 +54,11 @@ namespace Mustache
{
yield break;
}
int index = 0;
foreach (object item in enumerable)
{
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer };
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer, Data = index };
++index;
}
}
@ -66,7 +68,7 @@ namespace Mustache
/// <returns>The name of the tags that are in scope.</returns>
protected override IEnumerable<string> GetChildTags()
{
return new string[] { };
return new string[] { "index" };
}
/// <summary>

View File

@ -33,6 +33,8 @@ namespace Mustache
_tagLookup.Add(elseDefinition.Name, elseDefinition);
EachTagDefinition eachDefinition = new EachTagDefinition();
_tagLookup.Add(eachDefinition.Name, eachDefinition);
IndexTagDefinition indexDefinition = new IndexTagDefinition();
_tagLookup.Add(indexDefinition.Name, indexDefinition);
WithTagDefinition withDefinition = new WithTagDefinition();
_tagLookup.Add(withDefinition.Name, withDefinition);
}

View File

@ -80,7 +80,7 @@ namespace Mustache
scope.KeyNotFound += handler;
}
StringWriter writer = new StringWriter(provider);
_generator.GetText(scope, writer);
_generator.GetText(scope, writer, null);
return writer.ToString();
}
}

View File

@ -13,7 +13,8 @@ namespace Mustache
/// </summary>
/// <param name="scope">The current lexical scope of the keys.</param>
/// <param name="writer">The text writer to send all text to.</param>
/// <param name="contextData">The data associated to the context.</param>
/// <returns>The generated text.</returns>
void GetText(KeyScope scope, TextWriter writer);
void GetText(KeyScope scope, TextWriter writer, object contextData);
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Mustache
{
/// <summary>
/// Defines a tag that outputs the current index within an each loop.
/// </summary>
internal sealed class IndexTagDefinition : InlineTagDefinition
{
/// <summary>
/// Initializes a new instance of an IndexTagDefinition.
/// </summary>
public IndexTagDefinition()
: base("index")
{
}
/// <summary>
/// Gets whether the tag only exists within the scope of its parent.
/// </summary>
protected override bool GetIsContextSensitive()
{
return true;
}
/// <summary>
/// Gets the text to output.
/// </summary>
/// <param name="writer">The writer to write the output to.</param>
/// <param name="arguments">The arguments passed to the tag.</param>
/// <param name="contextData">Extra data passed along with the context.</param>
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
{
int index = (int)contextData;
writer.Write(index);
}
}
}

View File

@ -23,10 +23,10 @@ namespace Mustache
_arguments = arguments;
}
void IGenerator.GetText(KeyScope scope, TextWriter writer)
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
{
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
_definition.GetText(writer, arguments);
_definition.GetText(writer, arguments, contextData);
}
}
}

View File

@ -42,7 +42,7 @@ namespace Mustache
return formatBuilder.ToString();
}
void IGenerator.GetText(KeyScope scope, TextWriter writer)
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
{
object value = scope.Find(_key);
writer.Write(_format, value);

View File

@ -45,5 +45,10 @@ namespace Mustache
get;
set;
}
/// <summary>
/// Gets or sets data associated with the context.
/// </summary>
public object Data { get; set; }
}
}

View File

@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyVersion("0.1.2.0")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")]

View File

@ -46,7 +46,7 @@ namespace Mustache
}
}
void IGenerator.GetText(KeyScope scope, TextWriter writer)
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
{
writer.Write(Value);
}

View File

@ -152,7 +152,8 @@ namespace Mustache
/// </summary>
/// <param name="writer">The text writer to write to.</param>
/// <param name="arguments">The arguments passed to the tag.</param>
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments)
/// <param name="contextData">The data associated to the context.</param>
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
{
}

View File

@ -40,6 +40,7 @@
<Compile Include="ContentTagDefinition.cs" />
<Compile Include="Context.cs" />
<Compile Include="ContextParameter.cs" />
<Compile Include="IndexTagDefinition.cs" />
<Compile Include="KeyFoundEventArgs.cs" />
<Compile Include="InlineTagDefinition.cs" />
<Compile Include="EachTagDefinition.cs" />