public override async Task <object> Evaluate(EvaluationContext ctx) { var listCandidate = await ListCandidate.Evaluate(ctx); if (listCandidate == null) { // Template indexer implements null propagation out of the box return(null); } if (listCandidate is IList list) { if (Index < 0 || Index >= list.Count) { // Template indexer implements null propagation out of the box return(null); } return(list[Index]); } else { throw new TemplateException($"Indexer '#{Index}' is only valid on model entity lists."); } }
public override async Task GenerateOutput(StringBuilder builder, EvaluationContext ctx, Func <string, string> encodeFunc = null) { if (ListCandidate != null && Template != null) { var listObj = (await ListCandidate.Evaluate(ctx)) ?? new List <object>(); if (listObj is IList list) { int index = 0; foreach (var listItem in list) { // Initialize new evaluation context with the new variable in it var scopedCtx = ctx.Clone(); scopedCtx.SetLocalVariable(IteratorVariableName, new EvaluationVariable( evalAsync: () => Task.FromResult(listItem), selectResolver: () => AsyncUtil.Empty <Path>(), // It doesn't matter when generating output pathsResolver: () => AsyncUtil.Empty <Path>() // It doesn't matter when generating output )); // Index, useful for setting line numbers scopedCtx.SetLocalVariable(IteratorVariableName + "_index", new EvaluationVariable( evalAsync: () => Task.FromResult <object>(index), selectResolver: () => AsyncUtil.Empty <Path>(), // It doesn't matter when generating output pathsResolver: () => AsyncUtil.Empty <Path>() // It doesn't matter when generating output )); // Run the template again on that context await Template.GenerateOutput(builder, scopedCtx, encodeFunc); index++; } } else { throw new TemplateException($"Foreach expression could not be applied. Expression ({ListCandidate}) does not evaluate to a list."); } } }