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

@ -87,6 +87,16 @@ If you need to print out a block of text for each item in a collection, use the
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. 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 ## 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. 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."); 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 #endregion
#region With #region With
@ -1038,7 +1052,7 @@ Last";
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } }; 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"]); 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 // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("0.1.1.0")] [assembly: AssemblyVersion("0.1.2.0")]
[assembly: AssemblyFileVersion("0.1.1.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); Dictionary<string, object> arguments = _arguments.GetArguments(scope);
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments); IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
@ -93,7 +93,7 @@ namespace Mustache
{ {
foreach (IGenerator generator in generators) 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) if (context.WriterNeedsConsidated)
{ {
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments)); writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));

View File

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

View File

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

View File

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

View File

@ -13,7 +13,8 @@ namespace Mustache
/// </summary> /// </summary>
/// <param name="scope">The current lexical scope of the keys.</param> /// <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="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> /// <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; _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); 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(); return formatBuilder.ToString();
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer) void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
{ {
object value = scope.Find(_key); object value = scope.Find(_key);
writer.Write(_format, value); writer.Write(_format, value);

View File

@ -45,5 +45,10 @@ namespace Mustache
get; get;
set; 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 // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.1.0")] [assembly: AssemblyVersion("0.1.2.0")]
[assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")] [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); writer.Write(Value);
} }

View File

@ -152,7 +152,8 @@ namespace Mustache
/// </summary> /// </summary>
/// <param name="writer">The text writer to write to.</param> /// <param name="writer">The text writer to write to.</param>
/// <param name="arguments">The arguments passed to the tag.</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="ContentTagDefinition.cs" />
<Compile Include="Context.cs" /> <Compile Include="Context.cs" />
<Compile Include="ContextParameter.cs" /> <Compile Include="ContextParameter.cs" />
<Compile Include="IndexTagDefinition.cs" />
<Compile Include="KeyFoundEventArgs.cs" /> <Compile Include="KeyFoundEventArgs.cs" />
<Compile Include="InlineTagDefinition.cs" /> <Compile Include="InlineTagDefinition.cs" />
<Compile Include="EachTagDefinition.cs" /> <Compile Include="EachTagDefinition.cs" />