can/merge
Minimally update nested data structures with the response from the server.
Deprecated 5.0
This behavior is built in to can/map.
canMergeBehavior( baseConnection )
Overwrites can/map's instance callbacks so they use can-diff/merge-deep/merge-deep. can-diff/merge-deep/merge-deep is able to make minimal changes to the nested properties of can-define instances and lists given raw data. E.g:
let existingStudent;
const classroom = ClassRoom.get( { id: 505 } ).then( function( instance ) {
instance.id; // 505
instance.students[ 0 ].id; // 15
instance.students[ 0 ].name; // 'Samantha Jones'
existingStudent = instance;
} );
// later in the program new information for the classroom is retrieved
ClassRoom.get( { id: 505 } ).then( function( instance ) {
instance.id; // 505
instance.students[ 0 ].id; // 15
instance.students[ 0 ].name; // 'Samantha Jones-Baker'
// true, if can-merge behavior is used.
// a new nested instance isn't created, instead it was updated with the changed fields
existingStudent === instance.students[ 0 ];
} );
To use can/merge, the connection's Map, List and any of their
nested types must be properly configured. That configuration is discussed in the
"Use" section below.
Parameters
- baseConnection
{Object}:can-connectconnection object that is having thecan/mergebehavior added on to it. Expects the can/map behavior to already be added to this base connection. If theconnecthelper is used to build the connection, the behaviors will automatically be ordered as required.
Returns
{Object}:
a can-connect connection containing the methods provided by can/merge.
Use
To use the can/merge behavior, you have to:
- Add the behavior after can/map, and
- Make sure all types, especially List type is properly configured.
Adding the can/merge behavior after can/map is pretty straightforward.
When you create a custom connection, create it as follows:
import canMergeBehavior from "can-connect/can/merge/merge";
import canMapBehavior from "can-connect/can/map/map";
const ClassRoom = DefineMap.extend( {
// ...
} );
ClassRoom.List = DefineList.extend( {
"#": ClassRoom
} );
ClassRoom.queryLogic = new set.Algebra( { /* ... */ } );
ClassRoom.connection = connect( [ /* ... */ , canMapBehavior, canMergeBehavior /* ... */ ], {
Map: ClassRoom,
List: ClassRoom.List
} );
For can-diff/merge-deep/merge-deep to merge correctly, it needs to know how to uniquely identify an instance and
be able to convert raw data to instances and lists.
map-deep-merge looks for this configuration on the .queryLogic and .connection properties of the
[can-define.types.TypeConstructor] setting on can-define types.
This is more easily understood in an example.
If the ClassRoom has a students property that is a list of Student instances like:
const ClassRoom = DefineMap.extend( {
students: Student.List
} );
To be able to uniquely identify Student instances within that list, make sure Student has an queryLogic property
that is configured with the identifier property:
Student = DefineMap.extend( { /* ... */ } );
Student.queryLogic = new set.Algebra( set.props.id( "_id" ) );
Also make sure that Student.List points its # definition to Student
like the following:
Student.List = DefineList.extend( {
"#": Student
} );
Note: the typical method used to create a Student is new Student(props).
However, if Students have a .connection, can-diff/merge-deep/merge-deep will use
Student.connection.hydrateInstance(props).
This is useful if Students should be looked up in the connection instanceStore.
For example, Student might have a connection that has an instanceStore, like:
Student.connection = baseMap( {
Map: Student,
List: Student.List,
url: "/services/students",
name: "students"
} );