Codemirror autocomplete and auto closing brackets doesnt trigger change event - javascript

I have the following problem. I've written a server and client scripts for node js that work as live collaboration code editing. 2 or more people can code in the same instance of CodeMirror editor. Until i have enabled autocomplete feature and auto closing brackets it was working perfect, but after i did it messed up the work. When you use autocomplete list or when bracket or tag will be closed by module not by you manually it will not be recognized as change. I have inspected an object that CodeMirror instance is returning and it doesnt contain change that have been done automatically. its not even strictly problem for node js beacuse if you want lets say, send changes to server via ajax and save in a file, it wont happen beacuse its not present in change object. Anyone had similiar problem and can help?
client code:
var appCM = CodeMirror.fromTextArea(document.getElementById('app-cm'), {
mode: 'text/html',
theme: "monokai",
styleActiveLine: true,
lineNumbers: true,
matchBrackets: true,
indentUnit: 4,
indentWithTabs: true,
autoCloseTags: true,
autoCloseBrackets: true,
matchTags: false,
extraKeys: {
"Ctrl-Space": "autocomplete",
"Ctrl-Q": function(appCM) {
appCM.foldCode(appCM.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
readOnly: access
});
appCM.on('change', function(i, op) {
socket.emit('change', op);
});
socket.on('change', function(data) {
appCM.replaceRange(data.text, data.from, data.to);
});
server code:
socket.on('change', function(op) {
if(op.origin === '+input' || op.origin === 'paste' || op.origin === '+delete') {
clients.forEach(function(client) {
if(client !== socket)
client.emit('change', op);
});
};
});

You are explicitly filtering out changes whose origin isn't one of input/paste/delete. Why are you doing that? You'll need to propagate all changes if you want peers to stay in sync.

Related

Angular-Slickgrid header menu is not visible even setting enableHeaderMenu option to true

I am using Angular-Slickgrid in my angular application.
Grid is working fine but I am facing issue with Headermenu.
When I run the application unable to see Headermenu icon on mousehover in any header.
I am using below gridoptions :
this.gridOptions = {
enableAutoResize: true,
enableCellNavigation: true,
autoEdit: false,
enableRowSelection: true,
rowHeight: 30,
editable: true,
enableGrouping: true,
forceFitColumns: true,
enableHeaderButton: true,
enableHeaderMenu: true,
gridMenu: {
hideExportCsvCommand: true,
hideExportTextDelimitedCommand: true,
hideExportExcelCommand: true,
hideClearAllSortingCommand: true,
hideForceFitButton: true,
onBeforeMenuShow: (a, b) => {
}
}
};
As you can see I have set enableHeaderMenu: true, even after this unable to see the header menu.
Below is the image my grid look like:
When I mousehover on any header unable to see header menu icon(on which I need to click to open the header menu)
I have added the reference of required css files also and I think css is working.
Below is code of angular.json file:
"styles": [
"src/styles.scss",
"./node_modules/font-awesome/css/font-awesome.css",
"./node_modules/bootstrap/dist/css/bootstrap.css",
"./node_modules/flatpickr/dist/flatpickr.css",
"./node_modules/#fortawesome/fontawesome-free-webfonts/css/fontawesome.css",
"./node_modules/#fortawesome/fontawesome-free-webfonts/css/fa-regular.css",
"./node_modules/#fortawesome/fontawesome-free-webfonts/css/fa-brands.css",
"./node_modules/#fortawesome/fontawesome-free-webfonts/css/fa-solid.css",
"src/slickgrid-styles.scss",
"./node_modules/angular-slickgrid/lib/multiple-select/multiple-select.css"
],
"scripts": [
"./node_modules/jquery/dist/jquery.js",
"./node_modules/jquery-ui-dist/jquery-ui.min.js",
"./node_modules/slickgrid/lib/jquery.event.drag-2.3.0.js",
"./node_modules/bootstrap/dist/js/bootstrap.js",
"./node_modules/angular-slickgrid/lib/multiple-select/multiple-select.js"
]
code of slickgrid-styles.scss file :
#import './node_modules/angular-slickgrid/styles/sass/slickgrid-theme-bootstrap.scss';
After investigatng my code finally I found the route cause for the issue.
I am using 12 columns in my grid out of which I want to display only some columns on page load say 9 columns(user can the check the column from grid menu to make visible).For this purpose I am using below code:
setGridDefaultVisibleColumns() {
const visibleColumns = this.columnDefs.filter((c) => {
return c['visible'] !== false;
});
this.angularSilkGrid.slickGrid.setColumns(visibleColumns); }
I am calling this method in angularGridReady
angularGridReady(angularGrid: AngularGridInstance) {
this.dataGrid = angularGrid.slickGrid;
this.angularSilkGrid = angularGrid;
this.setGridDefaultVisibleColumns();
}
After commenting the setGridDefaultVisibleColumns function call my issue is resolved
Below is the workaround for the issue.setColumns method of slick gird is also affecting the headermenu.
I can hide the column like below without affecting the headermenu.See the below code
setGridDefaultVisibleColumns() {
this.columnDefs.forEach(c => {
if (c['visible'] === false) {
this.angularSilkGrid.extensionService.hideColumn(c);
}
}); this.angularSilkGrid.slickGrid.setColumns(visibleColumns);
}

Check if datatables is empty after table is fully loaded jQuery

After doing some research on this issue trying to find a solution (unfortunately to no avail) I decided that it might be best to post my problem. I currently have a Datatable containing asset information on a view and I'm trying to prevent a user from continuing change an option on a select field until an asset is added to the asset table.
Listed below is my assets datatable code in my .js file under the document.ready function
assets = $('#assets').dataTable({
sAjaxSource: "http://" + window.location.hostname + "/request/get-request-assets/?id=" + $('#id').val(),
fnDrawCallback: function (oSettings) {
setIncompleteTD();
enableRequestStatus();
},
scrollY: '185px',
scrollCollapse: true,
paging: false,
aaSorting: [[1, 'asc']],
bPaginate: false,
bFilter: false,
bLengthChange: false,
bAutoWidth: false,
bInfo: false,
aoColumns: [
{sWidth: '35px', bSortable: false},
{sWidth: '40px'},
{},
{},
{},
{},
{},
{sWidth: '80px'}
],
language: {
sLoadingRecords: '',
sEmptyTable: 'This request has no asset records.',
sInfoEmpty: ''
}
I've found table.fnSettings().aoData.length===0 as a means to check if a table is empty. However, after stepping through the code (via Chrome debugger) it seems Datatables (at least in my case) calls the function before the table is fully generated...
I have this code below
assetPresent = (assets.fnSettings().aoData.length===0) ? false : true;
console.log(assetPresent);
in my document.ready function after $('#assets').dataTable() function (if this matters). AssetPresent will be used as a flag to toggle the status select box. Unfortunately before I can utilize that...
console.log(assetPresent);
Seems to always be set to false even if there are clearly records in the table, and...
assetPresent = (assets.fnSettings().aoData.length===0) ? false : true;
Tends to be ignored...
I'm curious to see if table.fnSettings().aoData.length===0 might not be the best option. Thank you in advance.
Have you tried, table.data().length yet?
you can try this
var table = $('#assets').DataTable();
if ( ! table.data().any() ) {
alert( 'Empty table' );
}

Kendo UI custom grid popup editor window only opens once

I would like to use a custom window as popup editor of a Kendo UI Grid. Its content will be complex with search fields and a grid to display search results. To do that, I don't want to use the Kendo template mechanism but a real popup window.
While doing tests with custom editor I faced an issue. Even with a very basic and simple scenario (just the create command), I'm only able to open the editor once. The second time I get an error, the editor doesn't show up anymore and the grid becomes empty.
Here is my HTML code :
<div id="main-content">
<div id="custom-window">
</div>
<table id="my-table-grid">
</table>
</div>
The JavaScript part :
function openCustomWindow(e) {
e.preventDefault();
myWindow.center().open();
}
function editorWindowClosed(e) {
myGrid.cancelRow();
}
var myWindow = $("#custom-window").kendoWindow({
modal: true,
resizable: false,
title: "Test",
visible: false,
close: editorWindowClosed
}).data("kendoWindow");
var dummyDataSource = new kendo.data.DataSource(
{
schema : {
model : {
id: "id",
fields: {
postion: { type: "number"},
name: { type: "string"},
}
}
}
});
dummyDataSource.add({postion: 1, name: "gswin"});
var myGrid = $("#my-table-grid").kendoGrid({
dataSource: dummyDataSource,
toolbar: [ {
name: "create",
text: "Create"
} ],
editable: {
mode: "popup",
window: {
animation: false,
open: openCustomWindow,
}
},
columns: [
{
field: "postion",
title: "Postion"
},
{
field: "name",
title: "Name"
}
]
}).data("kendoGrid");
The error message in the JavaScript console :
Uncaught TypeError: Cannot read property 'uid' of
undefinedut.ui.DataBoundWidget.extend.cancelRow #
kendo.all.min.js:29ut.ui.DataBoundWidget.extend.addRow #
kendo.all.min.js:29(anonymous function) #
kendo.all.min.js:29jQuery.event.dispatch #
jquery-2.1.3.js:4430jQuery.event.add.elemData.handle #
jquery-2.1.3.js:4116
And finally a jsfiddle link to show what I'm doing. (The popup is empty because I just want to fix the open / close mechanism before going any further)
I don't understand why I get this error, I expected to be able to open / close the popup as many time as I wanted. The default editor popup is working fine.
One of my colleague found the solution (thanks to him).
Actually this piece of code
editable: {
mode: "popup",
window: {
animation: false,
open: openCustomWindow,
}
}
is designed to configure the default popup...
The right way to use a custom popup is the following :
editable :true,
edit: openCustomWindow,
With this approach it's also better to use
function editorWindowClosed(e) {
myGrid.cancelChanges();
}
Instead of
function editorWindowClosed(e) {
myGrid.cancelRow();
}
Working jsfiddle link
Actually the approach in my previous answer solved my issue but is causing another issue. The grid becomes editable but in the default mode (which is "inline").
Doing this
editable: "popup",
edit: openCustomWindow
has fixed this other issue but is now displaying 2 popups.
I finally succeeded to hide the default popup and only show my custom popup but it ended with the orginal issue described in initial question...
Considering all those informations I arrived to the conclusion that working with a custom popup window is definitely not an option. I'll start working with teamplates instead.
Another solution would have been to use a template to override the toolbar with a custom "Add" button and use a custom command for edition. But at this point I consider that too "tricky".
To prevent Kendo UI custom grid popup editor window, define the editable property:
editable:
{
mode: "popup",
window: {
animation: false,
open: updateRowEventHandler
}
} // skip edit property
Then paste preventDefault() at the beginning of the handler:
function updateRowEventHandler(e) {
e.preventDefault(); //

Why can't I change backdrop to 'true' or remove it after changing to 'static' in Bootstrap?

I'm trying to figure out why I can't change the backdrop to 'true' after first changing it to 'static' when using Bootstrap's modal.
I run this code:
$('#modal').modal({backdrop: 'static', keyboard: false, show: true});
$.post('server.php', $('#form').serialize(), function(data) {
if(data.success) {
$('#modal').modal({backdrop: true, keyboard: true});
} else {
$('#modal').modal({backdrop: true, keyboard: true});
}
}, 'json');
It will set it so the backdrop and keyboard will not function at the start, but I cannot figure out why I can't set it back to default.
Thanks for reading.
How about this
it looks like twitter bootstrap is storing all data in a data variable called bs.modal. so just remove that data variable and reinitialize the modal.
Your final code will look something like this
$('#modal').modal({backdrop: 'static', keyboard: false, show: true});
$.post('server.php', $('#form').serialize(), function(data) {
if(data.success) {
$('#modal').removeData('bs.modal').modal({backdrop: true, keyboard: true});
} else {
$('#modal').removeData('bs.modal').modal({backdrop: true, keyboard: true});
}
}, 'json');
The above solution was not working fine for me.
So I destroyed this instance of modal using this :-
$('#modal-xl').on('hidden.bs.modal', function (e) {
$("#modal-xl").modal('dispose');
})
Check working of dispose method here
Follow up to the solution presented by #Cerlin: I could not simply remove all data set to Bootstrap v4.6's modal, since I had other functionality attached to it. Using jQuery v3.6's $.extend(), I was able to modify the data directly to allow the backdrop/keyboard to be usable again. If you don't use jQuery and do not support Internet Explorer, you could always use JavaScript's built-in method Object.assign().
// Variable Defaults
var bootstrapModal = $('.modal');
var bootstrapData = bootstrapModal.data('bs.modal');
// Release Modal Backdrop
bootstrapModal.data('bs.modal', $.extend(true, bootstrapData, {
_config: $.extend(true, bootstrapData._config, {
backdrop: true,
keyboard: true
})
}));

Duplicate toastr error messages

I am using the Toastr 2.1 JavaScript library to display transient user input validation error messages. I set preventDuplicates option to true. It is not working -- I still see duplicate messages when users click validate button in rapid succession (clicks are faster than 'timeout').
Here are my toastr defaults:
function getDefaults() {
return {
tapToDismiss: true,
toastClass: 'toast',
containerId: 'toast-container',
debug: false,
showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
showDuration: 300,
showEasing: 'swing', //swing and linear are built into jQuery
onShown: undefined,
hideMethod: 'fadeOut',
hideDuration: 1000,
hideEasing: 'swing',
onHidden: undefined,
extendedTimeOut: 1000,
iconClasses: {
error: 'toast-error',
info: 'toast-info',
success: 'toast-success',
warning: 'toast-warning'
},
iconClass: 'toast-info',
positionClass: 'toast-top-right',
timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky
titleClass: 'toast-title',
messageClass: 'toast-message',
target: 'body',
closeHtml: '<button>×</button>',
newestOnTop: true,
preventDuplicates: true,
progressBar: false
};
}
Do i need to make any other changes to prevent duplicate error messages?
this may help
toastr.options = {
"preventDuplicates": true,
"preventOpenDuplicates": true
};
toastr.error("Your Message","Your Title",{timeOut: 5000});
I believe it's working as expected
preventDuplicates: Prevent duplicates of the **last toast**.
Perhaps this is the property you are looking for?
preventOpenDuplicates: Prevent duplicates of open toasts.
I had the same issue and it turned out that toastr preventDuplicates option does not work for array messages (current version 2.1.1). You need to convert the array to string using join.
I have the same requirements as you. Below is my implementation. See if it can help you.
function hasSameErrorToastr(message){
var hasSameErrorToastr = false;
var $toastContainer = $('#toast-container');
if ($toastContainer.length > 0) {
var $errorToastr = $toastContainer.find('.toast-error');
if ($errorToastr.length > 0) {
var currentText = $errorToastr.find('.toast-message').text();
var areEqual = message.toUpperCase() === currentText.toUpperCase();
if (areEqual) {
hasSameErrorToastr = true;
}
}
}
return hasSameErrorToastr;
}
//Usage
var message = 'Error deleting user';
if (hasSameErrorToastr(message)) {
toastr.error(message, title, errorToastrOptions);
}
The code is to check whether there are existing error toastr which has the same message being displayed. I will only fire the toastr.error if there is no existing instance of the same error on display. Hope this helps. The code can be refactored futher more but I'll leave it like this so that its more easier to understand for others.
imports: [
ToastrModule.forRoot({
timeOut: 10000,
positionClass: 'toast-bottom-right',
preventDuplicates: true,
}),
],
this is also present in npm for ngx-toastr documentation. you can add it in your app module.ts to see the change.
this may help.
var config = {
maxOpened: 1,
timeOut: 100
}
put it in your toastr config.and it should work.opened toastr is made to 1,and timeout set to 100.
Search preventDuplicates in toastr.min.js and change
preventDuplicates:!1
to
preventDuplicates:1
I was facing the same issue ngx-toastr and resolved by adding the below code in my module file.
ToastrModule.forRoot({
maxOpened: 1,
preventDuplicates: true,
autoDismiss: true
})
Also, if lazy loading is implemented, then you need to add the same lines of code to your parent module file also as I've added in my app.module.ts
Add preventDuplicates:1 to
toastr.options = {
maxOpened: 1,
preventDuplicates:1,
autoDismiss: true
};
I added this in the constructor and it worked for me
this.toastr.toastrConfig.preventDuplicates = true;

Categories

Resources