can-connect/can/ref/ref
Handle references to instances in the raw data returned by the server.
canRef( baseConnection )
Makes a reference type that is loads the related type or hold onto an existing one. This allows us to create circular references and load relevant data as needed
Parameters
- baseConnection
{connection}:The base connection should have can-connect/can/map/map already applied to it.
Use
can/ref is useful when the server might return either a reference to
a value or the value itself. For example, in a MongoDB setup, it
a request like GET /game/5 might return:
{
id: 5,
teamRef: 7,
score: 21
}
But a request like GET /game/5?$populate=teamRef might return:
{
id: 5,
teamRef: {id: 7, name: "Cubs"},
score: 21
}
can/ref can handle this abigutity, and even make lazy loading possible.
To use can/ref, first create a Map and a connection for the referenced type:
var Team = DefineMap.extend({
id: 'string'
});
connect([
require("can-connect/constructor/constructor"),
require("can-connect/constructor/store/store"),
require("can-connect/can/map/map"),
require("can-connect/can/ref/ref")
],{
Map: Team,
List: Team.List,
...
})
The connection is necessary because it creates an instance store which will
hold instances of Team that the Team.Ref type will be able to access.
Now we can create a reference to the Team within a Game map and the Game's connection:
var Game = DefineMap.extend({
id: 'string',
teamRef: {type: Team.Ref.type},
score: "number"
});
superMap({
Map: Game,
List: Game.List
})
Now, teamRef is a Map.Ref type, which will
house the id of the reference no matter how the server returns data like
game.teamRef.id.
For example, without populating the team data:
Game.get({id: 5}).then(function(game){
game.teamRef.id //-> 7
});
With populating the team data:
Game.get({id: 5, populate: "teamRef"}).then(function(game){
game.teamRef.id //-> 7
});
The values of other properties and methods on the Map.Ref type are determined by if the reference was populated or the referenced item already exists in the instanceStore.
For example, value, which points to the referenced instance will be populated if the reference was populated:
Game.get({id: 5, $populate: "teamRef"}).then(function(game){
game.teamRef.value.name //-> 5
});
Or, it will be populated if that instance had loaded through another means and is in the instance store:
Team.get({id: 7}).then(function(team){
// binding adds things to the store
team.on("name", function(){})
}).then(function(){
Game.get({id: 5}).then(function(game){
game.teamRef.value.name //-> 5
});
})
value is an asynchrounos getter, which means that even if
the referenced value isn't populated or loaded through the store, it can be lazy loaded. This
is generally most useful in a template.
The following will make an initial request for game 5, but when the template
tried to read and listen to game.teamRef.value.name, a request for team 7
will be made.
var template = stache("{{game.teamRef.value.name}} scored {{game.score}} points");
Game.get({id: 5}).then(function(game){
template({game: game});
});