What I did
I created a component in donejs and then created two supermodels contact
and email
using the following commands:
donejs add component contactComponent contact-component
donejs add supermodel contact
donejs add supermodel email
There's an API (feathers+mongodb) that provides contacts and emails. Each email has a contactId
.
The component includes the Contact
model and handles stuff like saving, creating new element, deleting an element, you name it. When combining this with the .stache
file it will succesfully retrive the elements from the API and list them accordingly.
So each Contact
has emails. As each contact has its own emails the contactComponent cannot directly get them but through the Contact
element.
This is where my design problem starts.
So far the contactComonent creates a viewmodel that handles the way contacts are handled. The contact model handles the API connection. This works fine and is scalable and clean.
But each contact needs a new model to load data (email) then I'm direcly using the model to manage all the logic related to emails. This does work but it seems that letting the model handle the connection and a viewmodel handling complex interactions is more appropriate for an MVVM design pattern.
I guess I'm not the first person doing this type of data modeling and I think there must be a better solution especially when dealing with a larger number of relationships and more complex ones.
I think what I currently have is something like this:
contactComponent.js
├── (includes) models/contact.js
│ ├── (includes) models/email.js
And this is what I'm looking for (I might be wrong)
contactComponent.js
├── (includes) models/contact.js
├── (includes / relates / references) emailComponent.js
emailComponent.js
├── (includes) models/email.js
Used files
File structurecontactComponent
├── contactComponent.js
├── contactComponent.stache
models
├── contact.js
├── email.js
contactComponent/contactComponent.js
/* contactComponent/contactComponent.js */
import Component from 'can/component/';
import Map from 'can/map/';
import 'can/map/define/';
import template from './contactComponet.stache!';
import Contact from '../models/contact.js';
export const ViewModel = Map.extend({
define: {
contactPromise: {
get: function() {
return Contact.getList({});
}
}
},
saveContact: function() {
// do some stuff
},
deleteContact: function() {
// do some stuff
}
});
export default Component.extend({
tag: 'contact-component',
viewModel: ViewModel,
template
});
contactComponent/contactComponent.stache
/* contactComponent/contactComponent.stache */
{{#if contactPromise.isResolved}}
{{#each contactPromise.value}}
Name: {{name}}
{{#if emailPromise.isResolved}}
Emails:
{{#each emailPromise.value}}
{{email}}
{{/each}}
{{/if}}
{{/each}}
{{/if}}
models/email.js
/* models/email.js */
import can from 'can';
import superMap from 'can-connect/can/super-map/';
import tag from 'can-connect/can/tag/';
import 'can/map/define/define';
export const Email = can.Map.extend({
define: {},
type: null,
email: null,
});
Email.List = can.List.extend({
Map: Email
}, {});
export const emailConnection = superMap({
url: '/api/modelEmail',
idProp: '_id',
Map: Email,
List: Email.List,
name: 'email'
});
tag('email-model', emailConnection);
export default Email;
This is where stuff starts getting too complex:
models/contact.js/* models/contact.js */
import can from 'can';
import superMap from 'can-connect/can/super-map/';
import tag from 'can-connect/can/tag/';
import 'can/map/define/define';
import Email from '../models/email.js';
export const Contact = can.Map.extend({
define: {
emailPromise: {
get: function() {
return Email.getList({ contactId: this.attr('id') });
}
}
},
name: null,
});
Contact.List = can.List.extend({
Map: Contact
}, {});
export const contactConnection = superMap({
url: '/api/modelContact',
idProp: '_id',
Map: Contact,
List: Contact.List,
name: 'contact'
});
tag('contact-model', contactConnection);
export default Contact;