Skip to content

Placeholders and Nesting

axunonb edited this page Jun 4, 2024 · 11 revisions

Named placeholders

string.Format references all args by index:
string.Format("{0} {1}", person.FirstName, person.LastName)

SmartFormat takes this a step further, and lets you use named placeholders instead:
Smart.Format("{FirstName} {LastName}", person)

In fact, SmartFormat supports several kinds of expressions (parameterless methods): Smart.Format("{FirstName.ToUpper} {LastName.ToLower}", person)
where ToUpper is a parameterless method.

The Special {} Placeholder

The {} placeholder is a special placeholder that refers to the current scope. It is often used with conditionals, plurals, and lists, but can also be used to access the current scope directly. Read more about this in the Nesting section.

var data = new { City = new { Region = "river side"}};
// Instead of writing:
var result = Smart.Format("The city region is {City.Region}.", data);
// You can also use a nested Placeholder
// which gets the scope of the parent Placeholder:
result = Smart.Format("{City.Region:The city region is {}.}", data);

Nesting

var person = new
{
    Person = new
    {
        FirstName = "John",
        LastName = "Long",
        Address = new { City = "London" }
    }
};

In SmartFormat, you can use nesting to avoid repetition, such as:

Smart.Format("City: {Person:{Address:{City}}, Name: {FirstName}}", person);
// Outputs: "City: London, Name: John"

Nesting is often used with conditionals, plurals, and lists:

var data = new 
{
    People = new List<object> 
    {
        new {Name = "Name 1", Age = 20}
    }
};
Smart.Format("There {People.Count:is a person.|are {} people.}", data);
// Outputs: "There is a person."

// After adding one more item to the People list:
// Outputs: "There are 2 people."

Nested Scope

When using nested placeholders, it is necessary to understand the scope that SmartFormat will use. A nested placeholder always starts off with the scope of the item that contains it.

The root scope for the template above is data, meaning that {User.Address} is equivalent to data.User.Address.

Within the nested area, however, the "scope" has changed to User.Address, so nested expressions like {Street} are evaluated against User.Address.

To illustrate this, the following are all equivalent:

  • {User.Name} {User.Address.City} {User.Address.State}
  • {User.Name} {User.Address:{City} {State}}
  • {User:{Name} {Address:{City} {State}}}

Within any nested scope, you still have access to the outer scopes. For example:
{User.Address:{User.Name} {City} {State}}
Here, {User.Name}, which is in the root scope, is still accessible from within the nested User.Address scope.

In the example above you'll notice the empty placeholder {}. It uses the scope of People.Count and outputs its value.

Clone this wiki locally