Nested Properties

Ractive uses the mustache syntax, which supports nested properties – in JavaScript-land, that means properties that are objects with their own properties (which might be objects with their own properties...).

Step 1

Let's say we were building an app that displayed information about different countries. An object representing a country could look like this:

{
  name: 'The UK',
  climate: { temperature: 'cold', rainfall: 'excessive' },
  population: 63230000,
  capital: { name: 'London', lat: 51.5171, lon: -0.1062 }
}

Add that data to our JavaScript – there's a placeholder country property.

We can refer to these nested properties in our template using dot notation. So to refer to the country's name, we use {{country.name}}.

Go ahead and replace the placeholders in the template with mustaches. Don't forget the href on the 'see map' link. If you get stuck, click the Fix Code button.

Execute the code. You should see an accurate description of the UK.

// Once we've rendered our view, we can change the country info
ractive.set( 'country', {
  name: 'Australia',
  climate: { temperature: 'hot', rainfall: 'limited' },
  population: 22620600,
  capital: { name: 'Canberra', lat: -35.2828, lon: 149.1314 }
});

Step 2

That's all well and good, but it's a little on the verbose side. You can imagine if we had lots more properties on the capital city object that we wanted to refer to – we don't want to keep writing {{country.capital.xyz}} if we don't have to.

We don't have to. Instead, we can use a with section to provide context:

{{#with country}}
  <p>{{name}} is a {{climate.temperature}} country
  with {{climate.rainfall}} rainfall and a population
  of {{population}}.</p>
{{/with}}

Strictly speaking, you don't need the with - you can just use a # sign by itself:

{{#country}}
  <p>{{name}} is a {{climate.temperature}} country
  with {{climate.rainfall}} rainfall and a population
  of {{population}}.</p>
{{/country}}

In this case, when Ractive looks up country, it will decide whether to render a with, if or each section based on its value. We'll learn about if and each shortly.

Generally, it's better to be explicit about which type of section you intend – other programmers (including future you) will be grateful.

Go ahead and update the template, creating a section for the capital as well. (You can either create a {{#with country.capital}} section, or a {{#with capital}} section inside the {{#with country}} section. Use whichever structure is easier in a given situation.)

Notice that if you create a {{#with capital}} section, you could end up having two {{name}} variables – one for the country, one for the capital.

We say that the capital {{name}} reference has a two-level context stack – if the innermost context (country.capital) has a name property, {{name}} resolves to the country.capital.name keypath.

If not, Ractive moves up the context stack (in this case, to country, and then to the root data object) until it does find a context with a name property. If no matching property is found, then the reference will resolve to the current context. Once a reference is resolved, its keypath is fixed.

If you ever need to force a reference to resolve in the current context, rather than potentially somewhere up the context stack, you can simply prefix the reference with a .. .stats.area will always resolve to country.capital.stats.area in {{#with country.capital}}{{.stats.area}}{{/with}}, even if there is not already a stats property on country.capital and there is one on country. If/when country.capital.stats is set, any references will be ready to update.

If you get stuck, hit the Fix Code button.

Step 3

Let's say we want to update a nested property. If we'd stored a reference to our model object, we could do it like this:

// we didn't store a reference, so let's do it now
var country = ractive.get('country');

country.climate.rainfall = 'very high';
ractive.update('country');

Ractive will recognise that only the rainfall property has changed, and leave everything else untouched.

But there's an easier way to do it:

ractive.set('country.climate.rainfall', 'too much');

Try changing properties. (If you're not from the UK, suitable values for rainfall include 'near-constant', 'unnecessarily high', or 'an unholy amount of'.)