"Unable to find key" warnings in CanJS 4.0

In recent versions of CanJS, we have updated the warnings that are given if can-stache can’t find a key you are trying to use. If you have recently upgraded CanJS, you’ve probably seen warnings like these:

This post will try to explain what causes these warnings and how to fix them.

There are a few different things that cause these warnings:

Attempted scope walking

If the warning you see contains suggestions for a “correct” key to replace what you are using, it probably means you are accidentally trying to walk the scope. These warnings look like this:

In CanJS 4.0, we removed the ability to walk up the scope to find a key, but we are trying to let you know this (in development) so that you can fix places where you’re still doing this.

For these warnings, you probably just want to pick the correct suggestion from the given options and use that in your code instead.

Typos

The simplest cause for warnings without suggestions is typos:

MyAppView:2: Unable to find key “tittle”.

Did you type {{tittle}} instead of {{title}} like I did? This one is an easy fix.

Properties that are only “sometimes” defined

A situation that is a little more difficult to diagnose is warnings for properties that are correct, but don’t exist all the time.

For example,

MyAppView:2: Unable to find key “first”.

is caused by this code:

{{# each(people) }}
    {{message}}, {{tittle}} {{first}} {{last}}!
{{/ each }} 

because not all the people have a first property:

people: {
    default() {
        return [{
            title: "Mr",
            first: "Emmet",
            last: "Brickowoski"
        }, {
            title: "Ms",
            first: "Wyldstyle"
        }, {
            title: "Lord",
            last:"Business"
        }];
    }
}

This can be fixed by making sure all the properties your template is using are defined. You can do this by filling them in with values:

people: {
    default() {
        return [{
            title: "Mr",
            first: "Emmet",
            last: "Brickowoski"
        }, {
            title: "Ms",
            first: "Wyldstyle",
            last: ""
        }, {
            title: "Lord",
            first: "",
            last:"Business"
        }];
    }
}

This can be time consuming and is very difficult if this is data coming from an API, so a better solution is to use observables that specifiy which keys exist. In this example this can be done by defining the People and Person types:

const Person = DefineMap.extend({
    title: "string",
    first: "string",
    last: "string"
});

const People = DefineList.extend({
    "#": Person
});

And then setting the people Type:

people: {
    Type: People,
    default() {
        return [ ... ];
    }
}

This makes sure that each item in the people array is a Person, which has the definitions for all of the properties we are using.

This same technique will work with any observable that implements canReflect.hasKey.

Other Warnings?

Are you seeing warnings that don’t fit in one of the categories above? Please let us know with a comment below or an issue in can-stache.

1 Like