示例#1
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    /// <exception cref="System.NotSupportedException">'where' should be a string expression but instead found '{oWhere.GetType().Name}'</exception>
    public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var htmlAttrs = await block.Argument.GetJsExpressionAndEvaluateAsync(scope) as Dictionary <string, object>;

        var         hasEach            = false;
        IEnumerable each               = null;
        var         binding            = "it";
        var         hasExplicitBinding = false;

        JsToken where = null;

        if (htmlAttrs != null)
        {
            if (htmlAttrs.TryGetValue("if", out var oIf))
            {
                if (DefaultScripts.isFalsy(oIf))
                {
                    return;
                }
                htmlAttrs.Remove("if");
            }

            if (htmlAttrs.TryGetValue(nameof(where), out var oWhere))
            {
                if (oWhere is not string whereExpr)
                {
                    throw new NotSupportedException($"'where' should be a string expression but instead found '{oWhere.GetType().Name}'");
                }

                where = whereExpr.GetCachedJsExpression(scope);
                htmlAttrs.Remove(nameof(where));
            }

            if (htmlAttrs.TryGetValue(nameof(each), out var oEach))
            {
                hasEach = true;
                htmlAttrs.Remove(nameof(each));
            }
            each = oEach as IEnumerable;

            if (htmlAttrs.TryGetValue("it", out var oIt) && oIt is string it)
            {
                binding            = it;
                hasExplicitBinding = true;
                htmlAttrs.Remove("it");
            }

            if (htmlAttrs.TryGetValue("class", out var oClass))
            {
                var cls = scope.Context.HtmlMethods.htmlClassList(oClass);
                if (string.IsNullOrEmpty(cls))
                {
                    htmlAttrs.Remove("class");
                }
                else
                {
                    htmlAttrs["class"] = cls;
                }
            }
        }

        var attrString = scope.Context.HtmlMethods.htmlAttrsList(htmlAttrs);

        if (HtmlScripts.VoidElements.Contains(Tag)) //e.g. img, input, br, etc
        {
            await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token).ConfigAwait();
        }
        else
        {
            if (hasEach)
            {
                var hasElements = each != null && each.GetEnumerator().MoveNext();
                if (hasElements)
                {
                    await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token).ConfigAwait();

                    var index      = 0;
                    var whereIndex = 0;
                    foreach (var element in each)
                    {
                        // Add all properties into scope if called without explicit in argument
                        var scopeArgs = !hasExplicitBinding && CanExportScopeArgs(element)
                                            ? element.ToObjectDictionary()
                                            : new Dictionary <string, object>();

                        scopeArgs[binding]       = element;
                        scopeArgs[nameof(index)] = AssertWithinMaxQuota(whereIndex++);
                        var itemScope = scope.ScopeWithParams(scopeArgs);

                        if (where != null)
                        {
                            var result = await where.EvaluateToBoolAsync(itemScope);
                            if (!result)
                            {
                                continue;
                            }
                        }

                        itemScope.ScopedParams[nameof(index)] = AssertWithinMaxQuota(index++);

                        await WriteBodyAsync(itemScope, block, token).ConfigAwait();
                    }

                    await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", token).ConfigAwait();
                }
                else
                {
                    await WriteElseAsync(scope, block.ElseBlocks, token).ConfigAwait();
                }
            }
            else
            {
                await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token).ConfigAwait();
                await WriteBodyAsync(scope, block, token).ConfigAwait();

                await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", token).ConfigAwait();
            }
        }
    }