jehugaleahsa / mustache-sharp

An extension of the mustache text template engine for .NET.
The Unlicense
306 stars 80 forks source link

how i add if condition like {{#if condition == true}} {{do somthing} {{/if} mustache sharp #94

Open ranaadeel0300 opened 2 years ago

ranaadeel0300 commented 2 years ago

how i solve the problem

{{#if condition == true}} {{do something}} {{/if} secondly i want to compare the value in if condition. how i do this

{{#if value == someothervalue}} {{do something}} {{/if}

blakepell commented 1 year ago

A little late but I thought I'd share for posterity.

You could make an equality tag that would be called like "{{#eq Age 45}} {{/eq}}". I will provide code for that. This just handles equals (you would need to do similiar for not equal, greater than, etc. if you wanted those).

/// <summary>
/// Defines a tag that conditionally prints its content, based on whether the passed in values are equal
/// </summary>
internal sealed class EqTagDefinition : ConditionTagDefinition
{
    private const string ConditionParameter = "condition";
    private const string TargetValueParameter = "targetValue";

    /// <summary>
    /// Initializes a new instance of a IfTagDefinition.
    /// </summary>
    public EqTagDefinition()
        : base("eq")
    { }

    /// <summary>
    /// Gets whether the tag only exists within the scope of its parent.
    /// </summary>
    protected override bool GetIsContextSensitive()
    {
        return false;
    }

    /// <summary>
    /// Gets the parameters that can be passed to the tag.
    /// </summary>
    /// <returns>The parameters.</returns>
    protected override IEnumerable<TagParameter> GetParameters()
    {
        return new[] {  new TagParameter(ConditionParameter) { IsRequired = true },
                        new TagParameter(TargetValueParameter){IsRequired = true}  };
    }

    /// <summary>
    /// Gets whether the primary generator group should be used to render the tag.
    /// </summary>
    /// <param name="arguments">The arguments passed to the tag.</param>
    /// <returns>
    /// True if the primary generator group should be used to render the tag;
    /// otherwise, false to use the secondary group.
    /// </returns>
    public override bool ShouldGeneratePrimaryGroup(Dictionary<string, object> arguments)
    {
        object condition = arguments[ConditionParameter];
        object targetValue = arguments[TargetValueParameter];
        return IsConditionSatisfied(condition, targetValue);
    }

    private bool IsConditionSatisfied(object condition, object targetValue)
    {
        // If both are null, they are equal
        if (condition == null && targetValue == null)
        {
            return true;
        }

        // If one of them is null, they are not equal
        if (condition == null || targetValue == null)
        {
            return false;
        }

        // If both are of the same type, compare them directly
        if (condition.GetType() == targetValue.GetType())
        {
            return condition.Equals(targetValue);
        }

        // Check if both are numeric types, convert them to decimals and compare
        if (IsNumeric(condition) && IsNumeric(targetValue))
        {
            decimal decimalCondition = Convert.ToDecimal(condition);
            decimal decimalTarget = Convert.ToDecimal(targetValue);

            return decimalCondition == decimalTarget;
        }

        // If they are of different types and not both numeric, they are not equal
        return false;
    }

    /// <summary>
    /// If the type of the object is numeric based on type (not value, e.g. strings
    /// are not numeric even if they are numbers).
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static bool IsNumeric(object obj)
    {
        switch (Type.GetTypeCode(obj.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.SByte:
            case TypeCode.Single:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return true;
            default:
                return false;
        }
    }

    /// <summary>
    /// Gets the parameters that are used to create a new child context.
    /// </summary>
    /// <returns>The parameters that are used to create a new child context.</returns>
    public override IEnumerable<TagParameter> GetChildContextParameters()
    {
        return Array.Empty<TagParameter>();
    }
}

Remember, you have to register that tag for it to work. I changed my registrations to load via reflection so I don't have to update it every time I add a tag definition (this works great as long as you're not doing AOT, and still might work with AOT but also might be finicky). If you want to do that you can change the constructor in FormatCompiler to:

/// <summary>
/// Initializes a new instance of a FormatCompiler.
/// </summary>
public FormatCompiler()
{
  // Reflect over all of the ITagDefinition's that are available.
  var type = typeof(ITagDefinition);
  var types = AppDomain.CurrentDomain.GetAssemblies()
                       .SelectMany(x => x.GetTypes())
                       .Where(x => type.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);

  foreach (var t in types)
  {
      var tag = (ITagDefinition)Activator.CreateInstance(t, true);
      _tagLookup.Add(tag.Name, (TagDefinition)tag);
  }
}