slickgrid custom dropdown editor - javascript

I am working a on slickgrid where one cell needs to show a dropdown (selectlist) with values coming from a restful service. These values are different for each row.
I should be able to select one value from this list (dropdown) and it should persist the value in the cell.
I would be glad if someone could help with this problem.
this.productColumns = [
{
id: 'statusid',
name: 'Status',
field: 'statusid',
width: 65,
sortable: true
},
{
id: 'grade',
name: 'Grade',
field: 'grade',
width: 80,
sortable: true
},
{
id: 'position',
name: 'Position',
field: 'originalPosition',
width: 80,
sortable: true
},
{
id: 'tyresize',
name: 'Tyre Size',
field: 'tyreSize',
editable: true,
width: 140,
sortable: true
},
{
id: 'tyredetail',
name: 'Tyre Detail',
field: 'tyredetail',
editable: true,
width: 125,
editor: this.selectRangeEditor
}
]
selectRangeEditor: function (args) {
var $select = $("");
var defaultValue = "";
var scope = this;
this.init = function () {
var tyreOptionsList = new TyreOptionsModel(args.item.id);
tyreOptionsList.deferred.done(function () {
var opt_values = tyreOptionsList.toJSON();
var count = 0;
for (var cnt in opt_values) {
if (opt_values.hasOwnProperty(cnt)) {
count++;
}
}
option_str = ""
var i ;
for (i = 0; i < count-1; i++) {
val = opt_values[i].tyreOptionId;
txt = opt_values[i].tyreDetail;
option_str += "<OPTION value='" + val + "'>" + txt + "</OPTION>";
}
$select = $("<SELECT tabIndex='0' class='editor-select'>" + option_str + "</SELECT>");
$select.appendTo(args.container);
$select.focus();
});
};
this.destroy = function () {
$(args.container).empty();
};
this.focus = function () {
$select.focus();
};
this.serializeValue = function () {
return $select.val();
};
this.applyValue = function (item, state) {
item.attributes[args.column.field] = state;
};
this.loadValue = function (item) {
defaultValue = item.attributes[args.column.field];
$select.val(defaultValue);
};
this.isValueChanged = function () {
return ($select.val() != defaultValue);
};
this.validate = function () {
return {
valid: true,
msg: null
};
};
this.init();
return $select.val();
}

Did you see my answer to this question ?
If you are able to get the options from the rest service for each row while generating the page, you can just use my solution at the client side ...
As I understand from you comment, the problem is how to postback the changes made in the grid after the user changed some fields ...
I solved this by adding the following piece of code, notice the JS code for the form submit, handle this incoming data at the server side, to save it using the RESTfull service.
<div id="myGrid" style="width:90%;height:250px;"></div>
<form action="" method="POST">
<input id="save_grid_changes" disabled="disabled" type="submit" value="Save changes to {{obj_type}}(s)">
<input type="hidden" name="obj_type" value="{{obj_type}}">
<input type="hidden" name="data" value="">
</form>
<script>
$(document).ready(function() {
grid = new Slick.Grid($("#myGrid"), griddata, columns, options);
$("form").submit(
function() {
// commit the last edit ...
grid.getEditController().commitCurrentEdit();
grid.resetCurrentCell();
$("input[name='data']").val( $.toJSON(griddata) );
// ("input[name='data']").val($.param(data));
});
});
</script>

Related

Determine when dojo dijit dialog and all widgets have completed first load/populate

Ok, I have what may be seem like a very dumb question to many of you, but I'm stumped. I'm working on a somewhat dated dojo project. Part of it has been converted to AMD and part hasn't. This part hasn't, so please bear with me.
I have a page that loads with a dgrid and has the shell of a dijit.Dialog built but hidden in html markup. There is a edit button on the page that when pressed, builds and populates the dialog and it's associated dijits (Selects, multiselects, etc. via old dojox.data.XmlStores and uses the data from the selected grid row to select the correct data in each dijit. As the dijits are built, onChange events are added. when the data is set in these dijits, the onChange events fire. This is needed. Sometimes one dijit's value needs to change another dijit's value...even on the first load. However, I have a couple extra javascript funtions that are tied to onChange events and set values of fields based on other values on the page. These DO NOT need to run on the first full load of the dialog and all its fields.
Here's what I think are the relevant pieces of my code:
The HTML markup:
<div class="dijitHidden">
<div data-dojo-type="dijit.Dialog" style="width:800px;text-align:left;" id="editDialog">
<table width='800px' cellpadding='25px' cellspacing='10px'>
<tr>
<td align="right"><From: </td>
<td align="left">
<div id="fromSelector"></div>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td align="right">Unit: </td>
<td align="left">
<div id="fromUnitSelector"></div>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td align="right">Room: </td>
<td align="left">
<div id="fromRoomSelector"></div>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td align="right">Mode: </td>
<td align="left" id="mode">
<div id="modeSelector"></div>
</td>
<td align="right">Priority:</td>
<td align="left" id="priority">
<div id="prioritySelector"></div>
</td>
</tr>
<tr>
<td></td>
<td></td>
<td align="right">Transporters: </td>
<td align="left" id="numSel">
<div id="numTranSelector"></div>
</td>
</tr>
<tr>
<td align="right">Type: </td>
<td align="left" id="type">
<div id="typeSelector"></div>
</td>
<td align="right">Comments:</td>
<td align="left" id="comment">
<div id="comments"></div>
</td>
</tr>
</table>
</div>
</div>
Javascript:
function edit_button_pressed() {
//Javascript that creates the boxes
//begin From Location
dojo.byId("fromSelector").innerHTML = "";
var fromSelector = dojo.byId("fromSelector");
var locStore = new dojox.data.XmlStore({
url: 'getlocations.url',
label: 'label',
keyAttribute:'id'
});
function sizeLocs(size, request){
var requests = size;
if (requests > 0) {
//from
var fromSelect = new dijit.form.Select({
name: "fromSelect",
id: "fromSelect",
store: locStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(fromSelector);
fromSelect.startup();
fromSelect.addOption({label: '--Select a Loc--', value: ' '});
if (transFromLoc.length > 0) {
fromSelect.set('value', transFromLoc);
} else {
fromSelect.set('value', ' ');
}
if (disableBecauseStarted) {
fromSelect.set('disabled', 'disabled');
}
fromSelect.on("change", function(){
if ((String(fromSelect.value) != String(defaultOrigination)) || disableBecauseStarted) {
dijit.byId('fromUnitSelect').set('disabled', 'disabled');
dijit.byId('fromRoomSelect').set('disabled', 'disabled');
} else {
dijit.byId('fromUnitSelect').set('disabled', false);
dijit.byId('fromRoomSelect').set('disabled', false);
}
setAutoPriority();
})
}
}
//end from location
//Units Select
var unitStore = new dojox.data.XmlStore({
url: 'getUnits.url',
label: 'label',
keyAttribute:'id'
});
//from
var fromUnitSelect = new dijit.form.Select({
name: "fromUnitSelect",
id: "fromUnitSelect",
store: unitStore,
style: "width: 150px;",
labelAttr: "label"
}).placeAt(dojo.byId('fromUnitSelector'));
fromUnitSelect.startup();
fromUnitSelect.set('disabled', 'disabled');
fromUnitSelect.addOption({label: '--Unit--', value: ' '});
if (transFromUnit.length > 0) {
fromUnitSelect.set('value', transFromUnit);
} else {
fromUnitSelect.set('value', ' ');
}
fromUnitSelect.on("change", function(){
dijit.byId('fromRoomSelect').destroy();
var fromRoomStore = new dojox.data.XmlStore({
url: 'getRooms.url?UNIT=' + this.value,
label: 'label',
keyAttribute:'id'
});
var fromRoomSelect = new dijit.form.Select({
name: "fromRoomSelect",
id: "fromRoomSelect",
store: fromRoomStore,
style: "width: 150px;",
labelAttr: "label"
}).placeAt(dojo.byId('fromRoomSelector'));
fromRoomSelect.startup();
fromRoomSelect.addOption({label: '--Select a Room--', value: ' '});
if (transFromRoom.length > 0) {
fromRoomSelect.set('value', transFromRoom);
} else {
fromRoomSelect.set('value', ' ');
}
if (String(dijit.byId('fromSelect').value) != String(defaultOrigination)) {
dijit.byId('fromUnitSelect').set('disabled', 'disabled');
dijit.byId('fromRoomSelect').set('disabled', 'disabled');
} else {
dijit.byId('fromUnitSelect').set('disabled', false);
dijit.byId('fromRoomSelect').set('disabled', false);
}
if (dijit.byId('patientSelect') != null
&& String(dijit.byId('patientSelect').value).length > 1 ) {
var patientDataStore = new dojox.data.XmlStore({
url: 'getPatient.url?MRN=' + String(dijit.byId('patientSelect').value),
rootItem: "patient"
});
var getPat = function(items, request){
for(var i = 0; i < items.length; i++){
var item = items[i];
var assignedRoom = String(patientDataStore.getValue(item, "assignedRoom"));
var reservationRoom = String(patientDataStore.getValue(item, "roomReservationRoom"));
}
if (reservationRoom.length > 0 ) {
setAssToRoom(reservationRoom);
setAssRoom(assignedRoom);
} else {
setAssToRoom(assignedRoom);
setAssRoom(assignedRoom);
}
}
var request = patientDataStore.fetch({query:{}, onComplete: getPat});
}
setAutoPriority();
})
//Begin Modes
dojo.byId("createModeSelector").innerHTML = "";
var createModeSelector = dojo.byId("createModeSelector");
var transModeStore = new dojox.data.XmlStore({
url: '/workassign/getTransModes.wa',
label: 'label',
keyAttribute:'id'
});
function transSizeMode(size, request){
var requests = size;
var modeSelect = "";
if (requests > 0) {
createModeSelect = new dijit.form.Select({
name: "createModeSelect",
id: "createModeSelect",
store: transModeStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(createModeSelector);
createModeSelect.startup();
createModeSelect.addOption({label: '--Select a Mode--', value: ' '});
}
}
transModeStore.fetch({query: {}, onBegin: transSizeMode, start: 0, count: 0});
//End Modes
//Begin Types
dojo.byId("typeSelector").innerHTML = "";
var typeSelector = dojo.byId("typeSelector");
var transTypeStore = new dojox.data.XmlStore({
url: 'getType.url',
label: 'label',
keyAttribute:'id'
});
var typeSel = new dijit.form.MultiSelect({
name: 'typeSelect',
id: 'typeSelect',
style: 'width: 200px; font-size: 18px;'
}).placeAt(typeSelector);
var fillTypes = function(items, request){
dojo.forEach(items, function(item) {
var c = dojo.doc.createElement('option');
c.innerHTML = transTypeStore.getValue(item, "label");
c.value = transTypeStore.getValue(item, "id");
typeSelect.appendChild(c);
})
if (transType.length > 0) {
var transArray = transType.split(",");
typeSel.set('value', transArray);
} else {
typeSel.set("value", " ");
}
}
typeSel.on("change", function(){
setAutoPriority();
setAutoTransporters();
})
typeSel.startup();
//End Types
dojo.byId("prioritySelector").innerHTML = "";
var prioritySelector = dojo.byId("prioritySelector");
var priorityModeStore = new dojox.data.XmlStore({
url: 'getPriorities.url',
label: 'label',
keyAttribute:'id'
});
function prioritySizeMode(size, request){
var requests = size;
var modeSelect = "";
if (requests > 0) {
prioritySelect = new dijit.form.Select({
name: "prioritySelect",
id: "prioritySelect",
store: priorityModeStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(prioritySelector);
prioritySelect.startup();
prioritySelect.addOption({label: '--Select a Priority--', value: ' '});
}
if (priorityType.length > 0) {
prioritySelect.set('value', priorityType);
} else {
prioritySelect.set('value', ' ');
}
if(userLevel=="TransScheduler") {
//Check priority override property
dojo.request.xhr("/workassign/getPriorityOverrideStatus.wa", {
handleAs: "text"
}).then(function(data){
if (data.indexOf("false") > -1) {
dijit.byId('prioritySelect').set('disabled',true);
}
}, function(err){
// Handle the error condition
alert("error: " + err);
}, function(evt){
// Handle a progress event from the request if the
// browser supports XHR2
});
}
}
//Fetches and populate data
transModeStore.fetch({query: {}, onBegin: transSizeMode, start: 0, count: 0});
transTypeStore.fetch({query: {}, onComplete: fillTypes});
priorityModeStore.fetch({query: {}, onBegin: prioritySizeMode, start: 0, count: 0});
locStore.fetch({query: {}, onBegin: sizeLocs, start: 0, count: 0});
//show the dialog
dijit.byId("editDialog").show();
} //end edit_button_pressed
function setAutoPriority() {
var params = "";
if (dijit.byId("fromUnitSelect")) {
params = params + "&FROMUNIT=" + dijit.byId("fromUnitSelect").get("value");
}
if (dijit.byId("toUnitSelect")) {
params = params + "&TOUNIT=" + dijit.byId("toUnitSelect").get("value");
}
if (dijit.byId("fromSelect")) {
params = params + "&FROMLOCATION=" + dijit.byId("fromSelect").get("value");
}
if (dijit.byId("toSelect")) {
params = params + "&TOLOCATION=" + dijit.byId("toSelect").get("value");
}
if (dijit.byId("typeSelect")) {
params = params + "&TYPE=" + dijit.byId("typeSelect").get("value");
}
dojo.request.xhr("getHighestPriority.URL?NUM=0" + params, {
handleAs: "text"
}).then(function(data){
if (dijit.byId("prioritySelect")) {
dijit.byId("prioritySelect").set('value', data);
}
}, function(err){
// Handle the error condition
alert("error: " + err);
}, function(evt){
// Handle a progress event from the request if the
// browser supports XHR2
});
}
function setAutoTransporters() {
var params = "";
if (dijit.byId("typeSelect")) {
params = params + "TYPES=" + dijit.byId("typeSelect").get("value");
}
dojo.request.xhr("getHighestNum.url?" + params, {
handleAs: "text"
}).then(function(data){
if (dijit.byId("numTransSelect")) {
dijit.byId("numTransSelect").set('value', data);
}
}, function(err){
// Handle the error condition
alert("error: " + err);
}, function(evt){
// Handle a progress event from the request if the
// browser supports XHR2
});
}
As you can see I have fetches that call the XmlStores and select what should be selected in the individual dijits when applicable. The problem functions for me are the setAutoPriority() and setAutoTransporters(). I could easily pass a variable to those functions to tell it whether or not it was the first load...or even wrap their call in an if statement. I just can't figure out how to tell that everything (populating the dijits with data and set their value) has happened.
Any ideas would be appreciated. Nothing I've come up with has worked so far. And just FYI, depending on the data from the grid row selected, not every one of those dijits will change/have data selected every time...which muddies the water even more.
So,
this is just a draft idea:
i cannot test it
it is dirty (there are better ways of doing)
But the general idea is there.
Core part of the idea are the first 4 lines of code.
var fetchDone = 0;
function isReady() {
return fetchDone >= 4; //the amount of fetch you have bellow
}
function edit_button_pressed() {
//Javascript that creates the boxes
//begin From Location
//reset the counter
fetchDone = 0;
dojo.byId("fromSelector").innerHTML = "";
var fromSelector = dojo.byId("fromSelector");
var locStore = new dojox.data.XmlStore({
url: 'getlocations.url',
label: 'label',
keyAttribute: 'id'
});
function sizeLocs(size, request) {
var requests = size;
if (requests > 0) {
//from
var fromSelect = new dijit.form.Select({
name: "fromSelect",
id: "fromSelect",
store: locStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(fromSelector);
fromSelect.startup();
fromSelect.addOption({
label: '--Select a Loc--',
value: ' '
});
if (transFromLoc.length > 0) {
fromSelect.set('value', transFromLoc);
} else {
fromSelect.set('value', ' ');
}
if (disableBecauseStarted) {
fromSelect.set('disabled', 'disabled');
}
fromSelect.on("change", function() {
if ((String(fromSelect.value) != String(defaultOrigination)) || disableBecauseStarted) {
dijit.byId('fromUnitSelect').set('disabled', 'disabled');
dijit.byId('fromRoomSelect').set('disabled', 'disabled');
} else {
dijit.byId('fromUnitSelect').set('disabled', false);
dijit.byId('fromRoomSelect').set('disabled', false);
}
setAutoPriority();
})
}
}
//end from location
//Units Select
var unitStore = new dojox.data.XmlStore({
url: 'getUnits.url',
label: 'label',
keyAttribute: 'id'
});
//from
var fromUnitSelect = new dijit.form.Select({
name: "fromUnitSelect",
id: "fromUnitSelect",
store: unitStore,
style: "width: 150px;",
labelAttr: "label"
}).placeAt(dojo.byId('fromUnitSelector'));
fromUnitSelect.startup();
fromUnitSelect.set('disabled', 'disabled');
fromUnitSelect.addOption({
label: '--Unit--',
value: ' '
});
if (transFromUnit.length > 0) {
fromUnitSelect.set('value', transFromUnit);
} else {
fromUnitSelect.set('value', ' ');
}
fromUnitSelect.on("change", function() {
dijit.byId('fromRoomSelect').destroy();
var fromRoomStore = new dojox.data.XmlStore({
url: 'getRooms.url?UNIT=' + this.value,
label: 'label',
keyAttribute: 'id'
});
var fromRoomSelect = new dijit.form.Select({
name: "fromRoomSelect",
id: "fromRoomSelect",
store: fromRoomStore,
style: "width: 150px;",
labelAttr: "label"
}).placeAt(dojo.byId('fromRoomSelector'));
fromRoomSelect.startup();
fromRoomSelect.addOption({
label: '--Select a Room--',
value: ' '
});
if (transFromRoom.length > 0) {
fromRoomSelect.set('value', transFromRoom);
} else {
fromRoomSelect.set('value', ' ');
}
if (String(dijit.byId('fromSelect').value) != String(defaultOrigination)) {
dijit.byId('fromUnitSelect').set('disabled', 'disabled');
dijit.byId('fromRoomSelect').set('disabled', 'disabled');
} else {
dijit.byId('fromUnitSelect').set('disabled', false);
dijit.byId('fromRoomSelect').set('disabled', false);
}
if (dijit.byId('patientSelect') != null && String(dijit.byId('patientSelect').value).length > 1) {
var patientDataStore = new dojox.data.XmlStore({
url: 'getPatient.url?MRN=' + String(dijit.byId('patientSelect').value),
rootItem: "patient"
});
var getPat = function(items, request) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
var assignedRoom = String(patientDataStore.getValue(item, "assignedRoom"));
var reservationRoom = String(patientDataStore.getValue(item, "roomReservationRoom"));
}
if (reservationRoom.length > 0) {
setAssToRoom(reservationRoom);
setAssRoom(assignedRoom);
} else {
setAssToRoom(assignedRoom);
setAssRoom(assignedRoom);
}
}
var request = patientDataStore.fetch({
query: {},
onComplete: getPat
});
}
setAutoPriority();
})
//Begin Modes
dojo.byId("createModeSelector").innerHTML = "";
var createModeSelector = dojo.byId("createModeSelector");
var transModeStore = new dojox.data.XmlStore({
url: '/workassign/getTransModes.wa',
label: 'label',
keyAttribute: 'id'
});
function transSizeMode(size, request) {
var requests = size;
var modeSelect = "";
if (requests > 0) {
createModeSelect = new dijit.form.Select({
name: "createModeSelect",
id: "createModeSelect",
store: transModeStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(createModeSelector);
createModeSelect.startup();
createModeSelect.addOption({
label: '--Select a Mode--',
value: ' '
});
}
}
transModeStore.fetch({
query: {},
onBegin: transSizeMode,
start: 0,
count: 0
});
//End Modes
//Begin Types
dojo.byId("typeSelector").innerHTML = "";
var typeSelector = dojo.byId("typeSelector");
var transTypeStore = new dojox.data.XmlStore({
url: 'getType.url',
label: 'label',
keyAttribute: 'id'
});
var typeSel = new dijit.form.MultiSelect({
name: 'typeSelect',
id: 'typeSelect',
style: 'width: 200px; font-size: 18px;'
}).placeAt(typeSelector);
var fillTypes = function(items, request) {
dojo.forEach(items, function(item) {
var c = dojo.doc.createElement('option');
c.innerHTML = transTypeStore.getValue(item, "label");
c.value = transTypeStore.getValue(item, "id");
typeSelect.appendChild(c);
})
if (transType.length > 0) {
var transArray = transType.split(",");
typeSel.set('value', transArray);
} else {
typeSel.set("value", " ");
}
}
typeSel.on("change", function() {
setAutoPriority();
setAutoTransporters();
})
typeSel.startup();
//End Types
dojo.byId("prioritySelector").innerHTML = "";
var prioritySelector = dojo.byId("prioritySelector");
var priorityModeStore = new dojox.data.XmlStore({
url: 'getPriorities.url',
label: 'label',
keyAttribute: 'id'
});
function prioritySizeMode(size, request) {
var requests = size;
var modeSelect = "";
if (requests > 0) {
prioritySelect = new dijit.form.Select({
name: "prioritySelect",
id: "prioritySelect",
store: priorityModeStore,
style: "width: 200px;",
labelAttr: "label"
}).placeAt(prioritySelector);
prioritySelect.startup();
prioritySelect.addOption({
label: '--Select a Priority--',
value: ' '
});
}
if (priorityType.length > 0) {
prioritySelect.set('value', priorityType);
} else {
prioritySelect.set('value', ' ');
}
if (userLevel == "TransScheduler") {
//Check priority override property
dojo.request.xhr("/workassign/getPriorityOverrideStatus.wa", {
handleAs: "text"
}).then(function(data) {
if (data.indexOf("false") > -1) {
dijit.byId('prioritySelect').set('disabled', true);
}
}, function(err) {
// Handle the error condition
alert("error: " + err);
}, function(evt) {
// Handle a progress event from the request if the
// browser supports XHR2
});
}
}
var onFetchComplete = function() {
fetchDone++;
};
//Fetches and populate data
transModeStore.fetch({
query: {},
onBegin: transSizeMode,
onComplete: onFetchComplete,
start: 0,
count: 0
});
transTypeStore.fetch({
query: {},
onComplete: function() {
onFetchComplete();
fillTypes.apply(this, arguments);
}
});
priorityModeStore.fetch({
query: {},
onBegin: prioritySizeMode,
onComplete: onFetchComplete,
start: 0,
count: 0
});
locStore.fetch({
query: {},
onBegin: sizeLocs,
onComplete: onFetchComplete,
start: 0,
count: 0
});
//show the dialog
dijit.byId("editDialog").show();
} //end edit_button_pressed
function setAutoPriority() {
if (!isReady()) {
return;
}
var params = "";
if (dijit.byId("fromUnitSelect")) {
params = params + "&FROMUNIT=" + dijit.byId("fromUnitSelect").get("value");
}
if (dijit.byId("toUnitSelect")) {
params = params + "&TOUNIT=" + dijit.byId("toUnitSelect").get("value");
}
if (dijit.byId("fromSelect")) {
params = params + "&FROMLOCATION=" + dijit.byId("fromSelect").get("value");
}
if (dijit.byId("toSelect")) {
params = params + "&TOLOCATION=" + dijit.byId("toSelect").get("value");
}
if (dijit.byId("typeSelect")) {
params = params + "&TYPE=" + dijit.byId("typeSelect").get("value");
}
dojo.request.xhr("getHighestPriority.URL?NUM=0" + params, {
handleAs: "text"
}).then(function(data) {
if (dijit.byId("prioritySelect")) {
dijit.byId("prioritySelect").set('value', data);
}
}, function(err) {
// Handle the error condition
alert("error: " + err);
}, function(evt) {
// Handle a progress event from the request if the
// browser supports XHR2
});
}
function setAutoTransporters() {
if (!isReady()) {
return;
}
var params = "";
if (dijit.byId("typeSelect")) {
params = params + "TYPES=" + dijit.byId("typeSelect").get("value");
}
dojo.request.xhr("getHighestNum.url?" + params, {
handleAs: "text"
}).then(function(data) {
if (dijit.byId("numTransSelect")) {
dijit.byId("numTransSelect").set('value', data);
}
}, function(err) {
// Handle the error condition
alert("error: " + err);
}, function(evt) {
// Handle a progress event from the request if the
// browser supports XHR2
});
}

How to implement heritage with knockoutJS objects

I have a model consisting of a project object, a text object and a setting object. The settings object can hold values for both projects and texts.
A text is always coupled to a project. Thus it should inherit the projects settings (if any). However if the text itself has settings, the user should be able to choose between using the project default or the text-specific settings.
The problem I'm having in my project is that updating the text local setting is also updating the project default settings. I guess this has to do with the observables relation, but so far I have been unable to find the connection.
Here's my code:
Text = function (data) {
var self = this;
self.textID = data.textID;
self.title = ko.observable(data.title);
self.projectID = data.projectID;
// settings
self.settings = ko.observable(); // initiated with "each" loop hook-up in viewmodel.
self.project = ko.observable(); // initiated with "each" loop hook-up in viewmodel.
self.projectSettings = ko.computed(function () {
var project = self.project();
if (project) {
var proset = project.settings();
return proset;
}
return null;
});
self.useProjectSettings = ko.observable(data.useProjectSettings || true);
self.hasLocalSettings = ko.observable(false); // initiated with each loop hook-up in viewmodel.
self.activeSettings = ko.computed(function () {
return self.useProjectSettings() ? self.projectSettings() : self.settings();
});
return self;
};
Project = function (data) {
var self = this;
self.projectID = data.projectID;
self.title = ko.observable(data.title);
// settings
self.settings = ko.observable();
return self;
};
Setting = function (data) {
var self = this;
self.settingID = data.settingID;
self.projectID = data.projectID;
self.textID = data.textID;
self.isVisible = ko.observable(data.isVisible);
self.isProjectDefault = ko.observable(data.isProjectDefault || true);
return self;
};
ViewModel = function (myprojects, mysettings, mytexts) {
var self = this,
koEach = ko.utils.arrayForEach, koFirst = ko.utils.arrayFirst, koFilter = ko.utils.arrayFilter;
self.selectedText = ko.observable();
//data - load
self.Projects = ko.observableArray(
ko.utils.arrayMap(myprojects, function (item) {
return new Project(item);
}));
self.Settings = ko.observableArray(
ko.utils.arrayMap(mysettings, function (item) {
return new Setting(item);
}));
self.Texts = ko.observableArray(
ko.utils.arrayMap(mytexts, function (item) {
return new Text(item);
}));
//alert("self.Projects()[0].projectID=" + self.Projects()[0].projectID );
// hook up 'settings' on 'project'
koEach(self.Settings(), function (s) {
if (s.isProjectDefault()) {
var project = koFirst(self.Projects(), function (p) {
return p.projectID === s.projectID;
});
if (project) {
project.settings(s);
}
}
});
//alert("self.Texts()[0].textID=" + self.Texts()[0].textID + ", self.Texts()[0].title()=" + self.Texts()[0].title() );
//hook up 'project' on 'texts'
koEach(self.Texts(), function (t) {
var project = koFirst(self.Projects(), function (p) {
return p.projectID === t.projectID;
});
t.project(project);
// initiate default publishingSettings for textbatch
var initsettings = project.settings();
t.settings(initsettings);
});
// hook up 'settings' on 'text'
koEach(self.Settings(), function (s) {
if (!s.isProjectDefault()) {
var text = koFirst(self.Texts(), function (t) {
return t.textID === s.textID;
});
if (text) {
text.settings(s);
}
}
});
};
// console log
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
// input
var projects = [{ projectID: 0, title: 'project 0' }, { projectID: 1, title: 'project 1' }];
var settings = [
{ settingID: 0, projectID: 1, textID: null, isVisible: true, isProjectDefault: true },
{ settingID: 1, projectID: null, textID: 0, isVisible: true, isProjectDefault: false },
{ settingID: 2, projectID: 1, textID: 1, isVisible: false, isProjectDefault: false }
];
var texts = [{ textID: 0, projectID: 0, title: 'first text' }, { textID: 1, projectID: 1, title: 'second text' }];
// binding
ko.applyBindings(new ViewModel(projects, settings, texts));
HereĀ“s my HTML:
<select data-bind="options: $root.Texts, optionsCaption: 'choose...', optionsText: 'title', value: selectedText"></select>
<br/>
Selected: <span data-bind="text: $root.selectedText().title"></span><br>
Selected2: <span data-bind="text: $root.selectedText().title"></span><br>
TextID: <span data-bind="text: selectedText().textID"></span><br>
<hr/>
<div data-bind="with: $root.selectedText">
<h2 data-bind="text:title"></h2>
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" data-bind="checked: useProjectSettings" />Use project settings
</label>
</div>
</div>
Settings for text '<span data-bind="text: title"></span>' (id='<span data-bind="value: textID"></span>').<br/><br/>
<div class="pubset" data-bind="css: {'unabled' : useProjectSettings() }">
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" data-bind="enable: !useProjectSettings(),checked: activeSettings().isVisible"/>
Show text
</label>
</div>
</div>
</div>
</div>
With my example code here, it seems I also have another problem, as it's not progressing to the second <span> node. Fiddle here.

Cannot reload data in Fuelux Datagrid

I have tried to reload the data populated by an ajax call but I cant get it to work, it shows the old data even after using the reload method. The thing is that if I change some variables to populate a different data and try to call the following code without refreshing the page it does not reload the updated data =/ Here is my code:
function populateDataGrid() {
$.ajaxSetup({async: false});
var gridinfo="";
$.post("lib/function.php",{activity: activity, shift: shift, date: date},
function (output){
gridinfo = JSON.parse(output);
});
$.ajaxSetup({async: true});
// INITIALIZING THE DATAGRID
var dataSource = new StaticDataSource({
columns: [
{
property: 'id',
label: '#',
sortable: true
},
{
property: 'date',
label: 'date',
sortable: true
},
....
],
formatter: function (items) {
var c=1;
$.each(items, function (index, item) {
item.select = '<input type="button" id="select'+c+'" class="select btn" value="select" onclick="">';
c=c+1;
});
},
data: gridinfo,
delay:300
});
$('#grid').datagrid({
dataSource: dataSource
});
$('#grid').datagrid('reload');
$('#modal-fast-appointment-results').modal({show:true});
}
I found a solution... I had to create a new DataSource (lets call it "AjaxDataSource") and add the ajax request functionality within the data constructor:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['underscore'], factory);
} else {
root.AjaxDataSource = factory();
}
}(this, function () {
var AjaxDataSource = function (options) {
this._formatter = options.formatter;
this._columns = options.columns;
this._delay = options.delay || 0;
this._data = options.data;
};
AjaxDataSource.prototype = {
columns: function () {
return this._columns;
},
data: function (options, callback) {
var self = this;
setTimeout(function () {
var data;
$.ajax({
url: 'getdata.php',
type: 'POST',
data: 'param1:param1,param2,param2,...,paramN:paramN', // this is optional in case you have to send some params to getdata.php
dataType: 'json',
async: false,
success: function(result) {
data = result;
},
error: function(data){
//in case we want to debug and catch any possible error
// console.log(data);
}
});
// SEARCHING
if (options.search) {
data = _.filter(data, function (item) {
var match = false;
_.each(item, function (prop) {
if (_.isString(prop) || _.isFinite(prop)) {
if (prop.toString().toLowerCase().indexOf(options.search.toLowerCase()) !== -1) match = true;
}
});
return match;
});
}
var count = data.length;
// SORTING
if (options.sortProperty) {
data = _.sortBy(data, options.sortProperty);
if (options.sortDirection === 'desc') data.reverse();
}
// PAGING
var startIndex = options.pageIndex * options.pageSize;
var endIndex = startIndex + options.pageSize;
var end = (endIndex > count) ? count : endIndex;
var pages = Math.ceil(count / options.pageSize);
var page = options.pageIndex + 1;
var start = startIndex + 1;
data = data.slice(startIndex, endIndex);
if (self._formatter) self._formatter(data);
callback({ data: data, start: start, end: end, count: count, pages: pages, page: page });
}, this._delay)
}
};
return AjaxDataSource;
}));
After defining the new DataSource, we just need to create it and call the datagrid as usual:
function populateDataGrid(){
// INITIALIZING THE DATAGRID
var dataSource = new AjaxDataSource({
columns: [
{
property: 'id',
label: '#',
sortable: true
},
{
property: 'date',
label: 'date',
sortable: true
},
....
],
formatter: function (items) { // in case we want to add customized items, for example a button
var c=1;
$.each(items, function (index, item) {
item.select = '<input type="button" id="select'+c+'" class="select btn" value="select" onclick="">';
c=c+1;
});
},
delay:300
});
$('#grid').datagrid({
dataSource: dataSource
});
$('#grid').datagrid('reload');
$('#modal-results').modal({show:true});
}
So now we have our datagrid with data populated via ajax request with the ability to reload the data without refreshing the page.
Hope it helps someone!

angularjs, select, add option

I am trying to add an option to a select element. The value is being given by a user form in a jquery ui modal.
When I use Chrome Developer tools, I can see that the bound object array is indeed getting the new object, but it's not showing up in the select element.
I used $('#company').scope().vendors in the console to bring up the array. It shows items being added to the array, but they aren't showing in the select box.
Here is what I have:
app.js
app.factory('Vendors', function(){
var Vendors = {};
Vendors.list = [
{
id: 1,
name: 'Company 1'
},
{
id: 2,
name: 'Company 2'
},
{
id: 3,
name: 'Company 3'
}
]
return Vendors;
})
app.controller('companyCtrl', function($scope, Vendors){
$scope.vendors = Vendors;
$scope.selectedVendor = 0;
$scope.companySelect = function(){
alert("You chose " + $scope.selectedVendor.name)
}
$scope.addCompany = function(name){
var maxId = 0;
for(var i=0; i<$scope.vendors.list.length; i++){
maxId = $scope.vendors.list[i].id;
}
newVendor = {id:++maxId, name:name};
$scope.vendors.list.push(newVendor)
$scope.selectedVendor = newVendor;
}
})
HTML
<div class="row">
<div class="grid_12" ng-controller="companyCtrl" id="company">
<span>Company</span>
<select ng-model="selectedVendor" ng-change="companySelect()" ng-options="v.name for v in vendors.list">
<option value="">-- Choose Company --</option>
</select>
<small><button onclick="openModal('addCompany');">Add</button></small>
</div>
</div>
Inline JS
$( "#addCompany" ).dialog({
autoOpen: false,
width: 350,
modal: true,
buttons: {
"Create new company": function() {
var name = $('#name').val();
if(name != ''){
$('#company').scope().addCompany(name);
}
$( this ).dialog( "close" );
},
Cancel: function() {
$( this ).dialog( "close" );
}
},
close: function() {
$('#name').val( "" );
}
});
function openModal(id){
$('#'+id).dialog('open');
}
I tried creating a jsFiddle, but I guess I'm not too sure how to get everything on there to work yet. But here is my try anyway: http://jsfiddle.net/aPXxe/
Try this:
$scope.$apply(function(){
$scope.vendors.list.push(newVendor);
$scope.selectedVendor = newVendor;
});

Knockout Complex Data Model Binding

Let's start with the code..
Javascript to bind viewmodel and display dialog
$("#add-song").click(function () {
$("<div>")
.attr("data-bind", "template: { name: 'manage-song-template' }")
.dialog({
resizable: false,
modal: true,
title: "Add Song",
width: 960,
height: 490,
buttons: [
{
text: "Create Song",
'data-bind': 'click: savechanges',
click: function () {
}
},
{
text: "Close",
click: function () {
$(this).dialog("close");
}
}
],
close: function () {
$(this).dialog('destroy').remove();
},
open: function () {
ko.applyBindings(new my.managesongviewmodel());
jquerybindings();
}
});
});
Now let's take a look at the view model
my.managesongviewmodel = function () {
var
//Scalar Properties
song = null,
//Functions
loadfromserver = function (Id) {
$.post("/song/getsong", "id=1", function (response) {
if (response.Success) {
var data = response.Data;
var song = response.Data.Song;
var songdata = {
Song: {
Id: song.Id,
Title: song.Title,
Description: song.Description,
Lyrics: song.Lyrics,
isMaster: song.isMaster,
AudioFilePath: song.AudioFilePath,
CoverImageFilePath: song.CoverImageFilePath,
CreatedDate: song.CreatedDate
},
Genres: data.Genres,
SongAlternateTitles: data.SongAlternateTitles,
Exploitations: data.Exploitations,
SongWriterSongs: data.SongWriterSongs
};
song = new my.song(songdata);
alert(song.title());
}
});
},
savechanges = function () {
};
loadfromserver();
return {
song: song,
loadfromserver: loadfromserver
};
};
my.song = function (data) {
var
//Scalar Properties
id = ko.observable(data.Song.Id),
title = ko.observable(data.Song.Title),
description = ko.observable(data.Song.Description),
lyrics = ko.observable(data.Song.Lyrics),
ismaster = ko.observable(data.Song.isMaster),
audiofilepath = ko.observable(data.Song.AudioFilePath),
coverimagefilepath = ko.observable(data.Song.CoverImageFilePath),
createddate = ko.observable(data.Song.CreatedDate),
//Arrays
genres = ko.observableArray(data.Genres),
alttitles = ko.observableArray(data.SongAlternateTitles),
exploitations = ko.observableArray(data.Exploitations),
songwritersongs = ko.observableArray(data.SongWriterSongs);
return {
id: id,
title: title,
description: description,
lyrics: lyrics,
ismaster: ismaster,
audiofilepath: audiofilepath,
coverimagefilepath: coverimagefilepath,
createddate: createddate,
genres: genres,
alttitles: alttitles,
exploitations: exploitations,
songwritersongs: songwritersongs
};
};
Here is my template
<script type="text/html" id="manage-song-template">
<div class="modal-form">
<span data-bind="text: song.title"></span>
</div>
</script>
Chrome is throwing the error "Cannot read property 'title' of null;" when I launch the dialog. That said, the "alert(song.title());" actually shows a the song title.
Any thoughts on where I've gone wrong?
Update
I have created a jsfiddle that replicates the issue. http://jsfiddle.net/mcottingham/H7jqa/28/
It's easy. In the moment when you shows modal window your song var still equals null. So you have to wait while info about song will be loaded from server:
$("<div>").attr("data-bind", "template: { name: 'manage-song-template' }").dialog({
// another options
open: function (event, ui) {
var vm = new my.managesongviewmodel(), domNode = this;
vm.loadfromserver().done(function() {
ko.applyBindings(vm, domNode);
jquerybindings();
});
}
})
// another code
And add return statement at the begining of loadfromserver function:
loadfromserver = function (Id) {
return $.post("/song/getsong", "id=1", function (response) {
// code
});
}

Categories

Resources