Mapping Knockout.js Observable Array with different ViewModels - javascript

I got the following sample response from server ( The original will be much more nested )
{
Widgets: [
{ type: 'Multivalue',value: ['val1','val2','val3'] },
{ type: 'OtherType',value: 'val', otherProp: 'prop' }
{ type: 'Text',value: 'text here' }
]
}
I know what are the available widget types so I have ViewModels for each widget as above:
var MultiValueVM = function(){
// some props
this.values = ko.observableArray()
}
var OtherValueVM = function(){
// some props
}
var TextValueVM = function(){
// some props
}
//More ViewModels for all types of widgets..
And my main ViewModel
var MainVM = function(){
//some props
this.widgets = ko.observableArray();
}
I'm in big confusion how to map the Collection of different objects from the server to my MainVM.widgets so that each widgets has its own ViewModel..

Related

How to properly share data between angular services

I'm attempting to rewrite a large and complex form that is doing everything in a controller. I started by separating related functions into their own modules/services. I don't understand how I am supposed to maintain the form data without crowding up the controller or requiring an excessive amount of arguments to be passed to the service function.
My current approach is to set variables on the service, then use that service in other services and try to access the saved data. This doesn't seem to be working. I think this is because injecting the service into another creates a new instance without all the saved values.
Here is a plunker that summarizes this approach: https://plnkr.co/edit/vyKtlXk8Swwf7xmoCJ4q
let app = angular.module('myApp', []);
app.service('productService', [function() {
let products = [
{ name: 'foo', value: 'foo' },
{ name: 'bar', value: 'bar' },
{ name: 'baz', value: 'baz' }
];
let selectedProduct = null;
this.getAvailableProducts = function() {
return products;
}
this.setSelectedProduct = function(product) {
selectedProduct = product;
}
}]);
app.service('storeService', ['productService', function(productService) {
let states = [
{ name: 'SC', value: 'SC' },
{ name: 'GA', value: 'GA' },
{ name: 'LA', value: 'LA' }
];
let selectedState = '';
this.getAvailableStates = function() {
return states;
}
this.setSelectedState = function(state) {
selectedState = state;
}
this.getPrice = function() {
// This console.log will always return undefined.
// productService.selectedProduct is not available.
console.log(productService.selectedProduct);
if (productService.selectedProduct == "foo" && selectedState == 'SC') {
return 10;
}
return 5;
}
}]);
app.controller('myController', function($scope, storeService, productService) {
$scope.name = '';
$scope.deliveryState = '';
$scope.selectedProduct = null;
$scope.price = 0;
$scope.productSelection = productService.getAvailableProducts();
$scope.states = storeService.getAvailableStates();
$scope.productChanged = function() {
productService.setSelectedProduct($scope.selectedProduct);
$scope.price = storeService.getPrice();
}
$scope.stateChanged = function() {
storeService.setSelectedState($scope.deliveryState);
$scope.price = storeService.getPrice();
}
});
I am trying to avoid something like this:
$scope.price = storeService.getPrice(
$scope.state,
$scope.selectedProduct,
$scope.servicePackage,
$scope.serviceFee,
$scope.shippingSelection,
// etc…
);
Should I be creating a third service that sets and gets all the data on the other services?
Should I just maintain all the data on the controller?
why do I get undefined when accessing a variable on the injected service?
The let declaration creates a private variable.
Add a getter for the variable:
app.service('productService', [function() {
let products = [
{ name: 'foo', value: 'foo' },
{ name: 'bar', value: 'bar' },
{ name: 'baz', value: 'baz' }
];
let selectedProduct = null;
this.getAvailableProducts = function() {
return products;
}
this.setSelectedProduct = function(product) {
selectedProduct = product;
}
//ADD getter
this.getSelectedProduct = function() {
return selectedProduct;
}
}]);
And use the getter:
this.getPrice = function() {
// This console.log will always return undefined.
// productService.selectedProduct is not available.
console.log(productService.selectedProduct);
̶i̶f̶ ̶(̶p̶r̶o̶d̶u̶c̶t̶S̶e̶r̶v̶i̶c̶e̶.̶s̶e̶l̶e̶c̶t̶e̶d̶P̶r̶o̶d̶u̶c̶t̶ ̶=̶=̶ ̶"̶f̶o̶o̶"̶ ̶&̶&̶ ̶s̶e̶l̶e̶c̶t̶e̶d̶S̶t̶a̶t̶e̶ ̶=̶=̶ ̶'̶S̶C̶'̶)̶ ̶{̶
if (productService.getSelectedProduct() == "foo" && selectedState == 'SC') {
return 10;
}
return 5;
}
Update
Should my services be communicating like that or is there a different, more accepted method?
I am trying to avoid something like this:
$scope.price = storeService.getPrice(
$scope.state,
$scope.selectedProduct,
$scope.servicePackage,
$scope.serviceFee,
$scope.shippingSelection,
// etc…
);
One way to avoid this is use an object as an argument to provide multiple options:
$scope.options = {};
$scope.price = storeService.getPrice(
$scope.selectedProduct,
$scope.options
);
The form can populate the options object directly:
<select ng-model="options.state">
<option ng-repeat="state in states">{{ state.name }}</option>
</select><br>
<select ng-model="options.serviceFee">
<option ng-repeat="fee in feeList">{{ fee.name }}</option>
</select><br>
<!-- //etc... -->
The setting of a variable in one service before computing something in another service creates an undesirable coupling that makes the code difficult to understand, debug, maintain, and test.
Instead all the information needed from the controller should be provided to the pricing service in a coherent manner.
You should not be injecting $scope, $scope is an outdated way of developing AngularJs and you should look into components or controllerAs syntax.
The controller should only be marshalling data between services and your view.
Services should provide data functions like get a product or create a new product and the controller should be doing things like
$ctrl = this;
$ctrl.product = productService.new();
or
$ctrl.product = productService.get(productId);
Then in your view you bind to properties of the product
<input name="name" ng-model="$ctrl.product.name">
And when you save a product you pass the whole thing back to the service
<form name="productForm" ng-submit="productForm.$valid && $ctrl.save()">
and in the controller
$ctrl.save = function() {
productService.save($ctrl.product);
}

Knockout.js Adding a Property to Child Elements

My code doesn't create a new property under the child element of knockout viewmodel that is mapped by knockout.mapping.fromJS.
I have:
//model from Entity Framework
console.log(ko.mapping.toJSON(model));
var viewModel = ko.mapping.fromJS(model, mappingOption);
ko.applyBindings(viewModel);
console.log(ko.mapping.toJSON(viewModel));
The first console.log outputs:
{
"Id": 0,
"CurrentUser": {
"BoardIds": [
{
"Id": 0
}
],
"Id": 1,
"UserName": "foo",
"IsOnline": true
},
"Boards": []
}
And then the mappingOption is:
var mappingOption = {
create: function (options) {
var modelBase = ko.mapping.fromJS(options.data);
modelBase.CurrentUser.UserName = ko.observable(model.CurrentUser.UserName).extend({ rateLimit: 1000 });
//some function definitions
return modelBase;
},
'CurrentUser': {
create: function (options) {
options.data.MessageToPost = ko.observable("test");
return ko.mapping.fromJS(options.data);
}
}
};
I referred to this post to create the custom mapping, but it seemed not working as the second console.log outputs the same JSON to the first one.
Also, I tried to create nested mapping option based on this thread and another one but it didn't work too.
var mappingOption = {
create: function (options) {
//modelBase, modifing UserName and add the functions
var mappingOption2 = {
'CurrentUser': {
create: function (options) {
return (new(function () {
this.MessageToPost = ko.observable("test");
ko.mapping.fromJS(options.data, mappingOption2, this);
})());
}
}
}
return ko.mapping.fromJS(modelBase, mappingOption2);
}
};
How can I correctly add a new property to the original viewmodel?
From the mapping documentation for ko.toJS (toJS and toJSON work the same way as stated in the document)
Unmapping
If you want to convert your mapped object back to a regular JS object, use:
var unmapped = ko.mapping.toJS(viewModel);
This will create an unmapped object containing only the properties of the mapped object that were part of your original JS object
If you want the json to include properties you've added manually either use ko.toJSON instead of ko.mapping.toJSON to include everything, or use the include option when first creating your object to specify which properties to add.
var mapping = {
'include': ["propertyToInclude", "alsoIncludeThis"]
}
var viewModel = ko.mapping.fromJS(data, mapping);
EDIT: In your specific case your mapping options are conflicting with each other. You've set special instructions for the CurrentUser field but then overridden them in the create function. Here's what I think your mapping options should look like:
var mappingOption = {
'CurrentUser': {
create: function (options) {
var currentUser = ko.mapping.fromJS(options.data, {
'UserName': {
create: function(options){
return ko.observable(options.data);
}
},
'include': ["MessageToPost"]
});
currentUser.MessageToPost = ko.observable("test");
return ko.observable(currentUser).extend({ rateLimit: 1000 });
}
}
};
and here's a fiddle for a working example

Mithril - how to populate drop down list of view from API

I'm trying to populate a drop down box rendered by Mithril's view from methods being called outside of its module (not sure if this terminology is correct, but outside of the property which contains the view, model and controller).
This Chrome extension adds a new field to an existing page and depending on what the user select, the drop down box should refresh to items pertaining to the selected item. I can get up to the stage of getting the new list of items, but i cannot get the drop down list to redraw with the new objects.
The following shows the module which gets inserted inside an existing page:
var ItemsList = {
model: function () {
this.list = function (id) {
var d = m.deferred()
// Calls Chrome extension bg page for retrieval of items.
chromeExt.getItems(pId, function (items) {
// Set default values initially when the controller is called.
if (items.length === 0) {
items = [
{name: 'None', value: 'none'}
]
}
d.resolve(items || [])
})
return d.promise
}
},
controller: function () {
this.model = new ItemsList.model()
this.index = m.prop(0)
this.onchange = function (e) {
console.info('ctrl:onchange', e.target)
}
// Initialise the drop down list array list.
this.dropDownItemsList = m.prop([]);
// This sets the default value of the drop down list to nothing by calling the function in the model,
// until the user selects an item which should populate the drop down list with some values.
this.getItems = function(pId) {
this.model.list(pId).then(function (data) {
this.dropDownItemsList(data)
m.redraw()
}.bind(this))
}
this.getItems(0);
},
view: function (ctrl) {
var SELECT_ID = 'record_select'
return vm.Type() ? m('div', [
m('.form__item', [
m('.label', [
m('label', {
htmlFor: SELECT_ID
}, 'ID')
]),
m('.field', [
m('select#' + SELECT_ID, {
onchange: ctrl.onchange.bind(ctrl)
},
ctrl.dropDownItemsList().map(function (it, i) {
return m('option', {
value: it.value,
checked: ctrl.model.index === i
}, it.name)
})
),
])
]),
]) : null
}
}
And it is mounted using
m.mount("element name here", ItemsList);
The code which checks to see if the item has changed is using a mutation observer, and whenever it detects changes to a certain field, it will call a method to get the new values. I can see that the return value has my new items.
I have tried various different methods on trying to update the drop down list, first by trying to set the "this.list" with the new items list i've got, or trying to create a returnable method on the controller which i can call when the mutation observer fires.
After getting the new items, how can i make the drop down list show the new items which has been retrieved?
I have read guides which shows functions in the controller or model being run - but only if they've been defined to use them already in the view (i.e. have an onclick method on the view which calls the method) but so far i cannot figure out how to update or call methods from outside of the module.
Is there a way to achieve the above or a different method i should approach this?
After some more research into how Mithril works, seems like that it's not possible to call any functions defined within the component.
Due to this, i have moved the model outside of the component (so now it only has the controller and the view defined) and bound the view to use the model outside of the component.
Now calling a function which updates the model (which is now accessible from elsewhere in the code) and redrawing shows the correct values that i need.
If I understand correctly, you need to have two variables to store your lists, one to store the old list and one to store the updated list so you can always map the updated one and go to your old one if you need.
Here is a simple implementation of a drop down list with some methods to update and search. You can update the list on the fly using the methods.
mithDropDown
jsFiddle
var MythDropDown = function(list) {
if (Array.isArray(list))
this.list = list;
else
list = [];
if (!(this instanceof MythDropDown))
return new MythDropDown(list);
var self = this;
this.selected = {
name: list[0],
index: 0
};
this.list = list;
};
MythDropDown.prototype.view = function(ctrl) {
var self = this;
return m('select', {
config: function(selectElement, isinit) {
if (isinit)
return;
self.selectElement = selectElement;
self.update(self.list);
},
onchange: function(e) {
self.selected.name = e.target.value;
self.selected.index = e.target.selectedIndex;
}
},
this.list.map(function(name, i) {
return m('option', name);
}));
};
MythDropDown.prototype.getSelected = function() {
return (this.selected);
};
MythDropDown.prototype.update = function(newList) {
this.list = newList;
this.selectElement.selectedIndex = 0;
this.selected.name = newList[0];
this.selected.index = 0;
};
MythDropDown.prototype.sort = function() {
this.list.sort();
this.update(this.list);
};
MythDropDown.prototype.delete = function() {
this.list.splice(this.selected.index, 1);
this.update(this.list);
};
var list = ['test option 1', 'test option 2'];
var myList = new MythDropDown(list);
var main = {
view: function() {
return m('.content',
m('button', {
onclick: function() {
var L1 = ['Banana', 'Apple', 'Orange', 'Kiwi'];
myList.update(L1);
}
},
'Fruits'),
m('button', {
onclick: function() {
var L1 = ['Yellow', 'Black', 'Orange', 'Brown', 'Red'];
myList.update(L1);
}
},
'Colors'),
m('button', {
onclick: function() {
myList.sort();
}
},
'Sort'),
m('button', {
onclick: function() {
myList.delete();
}
},
'Remove Selected'),
m('', m.component(myList),
m('', 'Selected Item: ' + myList.selected.name, 'Selected Index: ' + myList.selected.index)
)
);
}
};
m.mount(document.body, main);
<script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/0.2.3/mithril.min.js"></script>

Using Flux to build an edit form, who actually POSTs data to the server: actions, stores, views?

I have found a lot of resources, blogs, and opinions on how to fetch data for React and Flux, but much less on writing data to the server. Can someone please provide a rationale and some sample code for the "preferred" approach, in the context of building a simple edit form that persists changes to a RESTful web API?
Specifically, which of the Flux boxes should call $.post, where is the ActionCreator.receiveItem() invoked (and what does it do), and what is in the store's registered method?
Relevant links:
Should the action or store be responsible for transforming data when using React + Flux?
Should flux stores, or actions (or both) touch external services?
Where should ajax request be made in Flux app?
Short answer
Your form component should retrieve its state from Store, create "update" action on user inputs, and call a "save" action on form submit.
The action creators will perform the POST request and will trigger a "save_success" action or "save_error" action depending on the request results.
Long answer via implementation example
apiUtils/BarAPI.js
var Request = require('./Request'); //it's a custom module that handles request via superagent wrapped in Promise
var BarActionCreators = require('../actions/BarActionCreators');
var _endpoint = 'http://localhost:8888/api/bars/';
module.exports = {
post: function(barData) {
BarActionCreators.savePending();
Request.post(_endpoint, barData).then (function(res) {
if (res.badRequest) { //i.e response returns code 400 due to validation errors for example
BarActionCreators.saveInvalidated(res.body);
}
BarActionCreators.savedSuccess(res.body);
}).catch( function(err) { //server errors
BarActionCreators.savedError(err);
});
},
//other helpers out of topic for this answer
};
actions/BarActionCreators.js
var AppDispatcher = require('../dispatcher/AppDispatcher');
var ActionTypes = require('../constants/BarConstants').ActionTypes;
var BarAPI = require('../apiUtils/VoucherAPI');
module.exports = {
save: function(bar) {
BarAPI.save(bar.toJSON());
},
saveSucceed: function(response) {
AppDispatcher.dispatch({
type: ActionTypes.BAR_SAVE_SUCCEED,
response: response
});
},
saveInvalidated: function(barData) {
AppDispatcher.dispatch({
type: ActionTypes.BAR_SAVE_INVALIDATED,
response: response
})
},
saveFailed: function(err) {
AppDispatcher.dispatch({
type: ActionTypes.BAR_SAVE_FAILED,
err: err
});
},
savePending: function(bar) {
AppDispatcher.dispatch({
type: ActionTypes.BAR_SAVE_PENDING,
bar: bar
});
}
rehydrate: function(barId, field, value) {
AppDispatcher.dispatch({
type: ActionTypes.BAR_REHYDRATED,
barId: barId,
field: field,
value: value
});
},
};
stores/BarStore.js
var assign = require('object-assign');
var EventEmitter = require('events').EventEmitter;
var Immutable = require('immutable');
var AppDispatcher = require('../dispatcher/AppDispatcher');
var ActionTypes = require('../constants/BarConstants').ActionTypes;
var BarAPI = require('../apiUtils/BarAPI')
var CHANGE_EVENT = 'change';
var _bars = Immutable.OrderedMap();
class Bar extends Immutable.Record({
'id': undefined,
'name': undefined,
'description': undefined,
'save_status': "not saved" //better to use constants here
}) {
isReady() {
return this.id != undefined //usefull to know if we can display a spinner when the Bar is loading or the Bar's data if it is ready.
}
getBar() {
return BarStore.get(this.bar_id);
}
}
function _rehydrate(barId, field, value) {
//Since _bars is an Immutable, we need to return the new Immutable map. Immutable.js is smart, if we update with the save values, the same reference is returned.
_bars = _bars.updateIn([barId, field], function() {
return value;
});
}
var BarStore = assign({}, EventEmitter.prototype, {
get: function(id) {
if (!_bars.has(id)) {
BarAPI.get(id); //not defined is this example
return new Bar(); //we return an empty Bar record for consistency
}
return _bars.get(id)
},
getAll: function() {
return _bars.toList() //we want to get rid of keys and just keep the values
},
Bar: Bar,
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
},
});
var _setBar = function(barData) {
_bars = _bars.set(barData.id, new Bar(barData));
};
BarStore.dispatchToken = AppDispatcher.register(function(action) {
switch (action.type)
{
case ActionTypes.BAR_REHYDRATED:
_rehydrate(
action.barId,
action.field,
action.value
);
BarStore.emitChange();
break;
case ActionTypes.BAR_SAVE_PENDING:
_bars = _bars.updateIn([action.bar.id, "save_status"], function() {
return "saving";
});
BarStore.emitChange();
break;
case ActionTypes.BAR_SAVE_SUCCEED:
_bars = _bars.updateIn([action.bar.id, "save_status"], function() {
return "saved";
});
BarStore.emitChange();
break;
case ActionTypes.BAR_SAVE_INVALIDATED:
_bars = _bars.updateIn([action.bar.id, "save_status"], function() {
return "invalid";
});
BarStore.emitChange();
break;
case ActionTypes.BAR_SAVE_FAILED:
_bars = _bars.updateIn([action.bar.id, "save_status"], function() {
return "failed";
});
BarStore.emitChange();
break;
//many other actions outside the scope of this answer
default:
break;
}
});
module.exports = BarStore;
components/BarList.react.js
var React = require('react/addons');
var Immutable = require('immutable');
var BarListItem = require('./BarListItem.react');
var BarStore = require('../stores/BarStore');
function getStateFromStore() {
return {
barList: BarStore.getAll(),
};
}
module.exports = React.createClass({
getInitialState: function() {
return getStateFromStore();
},
componentDidMount: function() {
BarStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
BarStore.removeChangeListener(this._onChange);
},
render: function() {
var barItems = this.state.barList.toJS().map(function (bar) {
// We could pass the entire Bar object here
// but I tend to keep the component not tightly coupled
// with store data, the BarItem can be seen as a standalone
// component that only need specific data
return <BarItem
key={bar.get('id')}
id={bar.get('id')}
name={bar.get('name')}
description={bar.get('description')}/>
});
if (barItems.length == 0) {
return (
<p>Loading...</p>
)
}
return (
<div>
{barItems}
</div>
)
},
_onChange: function() {
this.setState(getStateFromStore();
}
});
components/BarListItem.react.js
var React = require('react/addons');
var ImmutableRenderMixin = require('react-immutable-render-mixin')
var Immutable = require('immutable');
module.exports = React.createClass({
mixins: [ImmutableRenderMixin],
// I use propTypes to explicitly telling
// what data this component need. This
// component is a standalone component
// and we could have passed an entire
// object such as {id: ..., name, ..., description, ...}
// since we use all the datas (and when we use all the data it's
// a better approach since we don't want to write dozens of propTypes)
// but let's do that for the example's sake
propTypes: {
id: React.PropTypes.number.isRequired,
name: React.PropTypes.string.isRequired,
description: React.PropTypes.string.isRequired
}
render: function() {
return (
<li> //we should wrapped the following p's in a Link to the editing page of the Bar record with id = this.props.id. Let's assume that's what we did and when we click on this <li> we are redirected to edit page which renders a BarDetail component
<p>{this.props.id}</p>
<p>{this.props.name}</p>
<p>{this.props.description}</p>
</li>
)
}
});
components/BarDetail.react.js
var React = require('react/addons');
var ImmutableRenderMixin = require('react-immutable-render-mixin')
var Immutable = require('immutable');
var BarActionCreators = require('../actions/BarActionCreators');
module.exports = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
id: React.PropTypes.number.isRequired,
name: React.PropTypes.string.isRequired,
description: React.PropTypes.string.isRequired
},
handleSubmit: function(event) {
//Since we keep the Bar data up to date with user input
//we can simply save the actual object in Store.
//If the user goes back without saving, we could display a
//"Warning : item not saved"
BarActionCreators.save(this.props.id);
},
handleChange: function(event) {
BarActionCreators.rehydrate(
this.props.id,
event.target.name, //the field we want to rehydrate
event.target.value //the updated value
);
},
render: function() {
return (
<form onSubmit={this.handleSumit}>
<input
type="text"
name="name"
value={this.props.name}
onChange={this.handleChange}/>
<textarea
name="description"
value={this.props.description}
onChange={this.handleChange}/>
<input
type="submit"
defaultValue="Submit"/>
</form>
)
},
});
With this basic example, whenever the user edits a Bar item via the form in BarDetail component, the underlying Bar record will be maintained up to date locally and when the form is submitted we try to save it on the server. That's it :)
Components/Views are used to display data and fire events
Actions are tied to the events (onClick, onChange...) and are used to communicate with resources and dispatch events once the promise has been resolved or failed. Make sure you have at least two events, one for success and one for ajax failed.
Stores are subscribed to the events dispatcher is dispatching. Once data is received stores are updating the values which are stored and emitting changes.
Components/Views are subscribed to the stores and are re-rendering once the change has happened.
Should flux stores, or actions (or both) touch external services? approach is what seems natural to me.
Also there are cases when you need to trigger some action as a result of some other action being triggered, this is where you can trigger actions from a relevant store, which results store and views being updated.

Why are my Backbone Models nested strangely within a Collection, requiring drilling down to access methods/properties?

I've got a Collection and a Model, both using attributes/options to augment them with additional capabilities. Here's the Model (LoadRouteGroup):
return Backbone.Model.extend({
initialize: function () {
console.log(this);
},
fetchf: function () {
console.log("FETCH");
}
});
And the Collection (LoadRouteGroups):
return Backbone.Collection.extend({
constructUrl: function(options) {
if (options.groupingType === "facility") {
// TODO: new endpoint: /api/v1/loadroutes?grouping=facility
this.url = clawConfig.endpoints.webApiRootUrl + "/api/loads/facilities";
}
else {
this.url = clawConfig.endpoints.webApiRootUrl + "/api/v1/loadroutes";
}
},
initialize: function (models, options) {
options || (options = {});
this.constructUrl(options);
console.log(this);
}
});
They're instantiated as such:
var loadRouteGroup = new LoadRouteGroup({
entityType: "facility"
});
// WORKS
loadRouteGroup.fetchf();
// assign groupingType option to collection to denote which URL to use
var loadRouteGroups = new LoadRouteGroups({
model: loadRouteGroup
}, {
groupingType: "facility"
});
var firstGroup = loadRouteGroups.at(0);
// DOESN'T WORK
firstGroup.fetchf();
// WORKS
firstGroup.attributes.model.fetchf();
I would expect that call to firstGroup.fetchf() to work... but it doesn't. Instead, I have to weirdly drill down and use firstGroup.attributes.model.fetchf() in order to access the method.
What's going on here? This would seem straightforward to me, but I can't for the life of me figure out what's wrong with the relationship between my Collection and Model.
The collection definition should include the model type:
return Backbone.Collection.extend({
// ....
model: LoadRouteGroup
});
When initializing the collection, pass in an array of models:
var loadRouteGroup = new LoadRouteGroup({
entityType: "facility"
});
var loadRouteGroups = new LoadRouteGroups([loadRouteGroup], {
groupingType: "facility"
});
Specify the model when you extend the collection instead of when you instantiate.

Categories

Resources