initSubmit potential bug using promise and MJoin options instance
initSubmit potential bug using promise and MJoin options instance
I tried to use initSubmit with a promise today to make sure my ajax call to make some database updates completes before Editor keeps going.
The database updates I make relate to an options instance in an MJoin. At the front end I use Selectize and the user is allowed to enter new values which of course is a problem because then text is passed to the server instead of selected ids.
What I wanted to do is to
a) make the database inserts of the values newly entered by the user to make sure the MJoin can make the link table inserts to them
b) replace the id array that contains text as well with an array that contains valid ids only
The result was that Editor didn't wait for the promise to be resolved but tried to make the MJoin link table inserts with the (by then non-replaced) text fields which led to a crash due to referential integrity constraints.
I think this is truly a bug; you might want to check this.
I don't know whether the promise would have worked if it wasn't about an MJoin. But Editor didn't wait for promise resolution when it processed the MJoin. I am sure because I traced it in the debugger. There were no other bugs.
I implemented a work around now. No (false) promises any longer ... I now use a combination of initSubmit and submitSuccess to get it done relying on a global variable to keep the ids until submit success.
So this is my work around:
Editor selectize field allowing new entries:
}, {
label: lang === 'de' ? 'Labels:' : 'Labels:',
name: "ctr_label[].id",
type: "selectize",
opts: {
create: true,
createFilter: function(val) {
return ( isNaN(val) || val.indexOf('.') > -1 || val.indexOf('-') > -1 ) ? val : false;
},
maxItems: null,
openOnFocus: true,
allowEmptyOption: false,
placeholder: lang === 'de' ?
"Bitte Labels wählen oder hinzufügen" :
"Please select labels or add some",
render: {
option_create: function(data, escape) {
var add = lang === 'de' ? "Neu: " : "Add ";
return '<div class="create">' + add + '<strong>'
+ escape(data.input) + '</strong>…</div>';
}
}
},
}, {
Editor initSubmit and submitSuccess events:
.on ( 'initSubmit', function ( e, action ) {
//new labels need to be filtered out because the Editor options
//instance will crash due to referential integrity
if ( action !== "remove" ) {
ids = ctrEditor.field('ctr_label[].id').val();
var sanitizedLabelIdArray = [];
for (i=0; i < ids.length; i++) {
if ( ( ! isNaN(ids[i]) ) && ids[i].indexOf('.') < 0
&& ids[i].indexOf('-') < 0 ) {
sanitizedLabelIdArray.push( ids[i] );
}
}
ctrEditor.field('ctr_label[].id').val(sanitizedLabelIdArray);
} else {
ids = [];
}
})
.on('submitSuccess', function (e, json, data, action) {
//we need to process potential new labels!
if ( action !== "remove") {
$.ajax({
type: "POST",
url: 'actions.php?action=processLabelIdArray',
data: {
//labelIdArray can also contain text of newly created labels!
ctrId: json.data[0].DT_RowId.substr(4),
labelIdArray: JSON.stringify(ids)
},
success: function () {
ajaxReloadTbls([ctrTable]);
ids = [];
}
});
} else {
ajaxReloadTbls([ctrTable]);
ids = [];
}
});
and the PHP Mjoin with options instance:
->join(
Mjoin::inst( 'ctr_label' )
->link( 'ctr.id', 'ctr_has_ctr_label.ctr_id' )
->link( 'ctr_label.id', 'ctr_has_ctr_label.ctr_label_id' )
->order( 'ctr_label.label_text asc' )
->fields(
Field::inst( 'id' )->set( false )
->options( Options::inst()
->table('ctr_label, user_has_available_label')
->value('ctr_label.id')
->label( 'ctr_label.label_text' )
->order( 'ctr_label.label_text asc' )
->where( function($q) {
$q ->where( function($r) {
$r ->where('user_has_available_label.user_id', $_SESSION['id']);
$r ->where('ctr_label.id', 'user_has_available_label.ctr_label_id', '=', false); //join
} );
//We want to be able to keep existing ctr labels even though they are not in the user's domain
$ctrId = 0;
if ( isset($_SESSION['ctr_id'] ) ) {
$ctrId = $_SESSION['ctr_id'];
}
$q ->or_where( 'ctr_label.id',
'( SELECT DISTINCT ctr_label_id
FROM ctr_has_ctr_label
WHERE ctr_id = :ctrId
)', 'IN', false);
$q ->bind( ':ctrId', $ctrId );
} )
),
Field::inst( 'label_text' )->set( false )
)
)
I use Editor 1.9.
Replacing the Editor field contents is no problem in my work around.
ctrEditor.field('ctr_label[].id').val(sanitizedLabelIdArray);
I tried to do the same with a promise - but Editor didn't wait for resolution of the promise.
This was (approx.) the code that didn't work:
.on ( 'initSubmit', function ( e, action ) {
return new Promise( function ( resolve ) {
$.ajax({
type: "POST",
url: 'actions.php?action=processLabelIdArray',
data: {
//labelIdArray can also contain text of newly created labels!
labelIdArray: JSON.stringify(ctrEditor.field('ctr_label[].id').val())
},
dataType: "json",
success: function (data) {
//the new array only contains real ids - no text any longer
ctrEditor.field('ctr_label[].id').val(data);
resolve(); //Editor continues after this!
}
});
});
})
Replies
That is what should happen. But from your description it isn't waiting for that. Is that correct? Are you able to give me a link to the page so I can take a look at the timing?
Thanks,
Allan
Unfortunately I can't Allan. I know that is a bit frustrating but maybe you can figure it out with a different example. Sorry. And yes, it isn't waiting for "resolve()".
Are you running the non-min version of Editor? Can you check this this code is present in the
_event
function:That is what should be used to allow for a Promise and Colin was testing this just the other week.
Allan
Yes this code is present in Editor. Just checked it.
I've just tried it here and it appears to work as expected: http://live.datatables.net/nuvihosi/11/edit .
The console shows:
with a 5 second wait between 1 and 2.
Allan
Tried promises again on "initSubmit" - and this time it works. I use a newer Editor version now, maybe that is the reason. Or maybe because I return "true" in the resolve statement. Don't know.