-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Executing command inside enqueueChanges() leads to confusing behavior #3865
Comments
This is confusing because setTimeout( () => {
selection.setRanges( [ range ] );
setTimeout( () => {
document.execute( 'bold' );
}, 1 );
selection.collapse( range.start );
}, 1 ); Unfortunately:
It's nice that BTW. remember that there is a ticket somewhere to make all commands create |
I've again tried to do this: editor.document.selection.setRanges( ranges );
editor.execute( commandOrCallback, { batch, forceValue: true } );
editor.document.selection.setTo( initialSelection ); Just to understand that it's wrong. So I changed it to: editor.document.enqueueChanges( () => {
editor.document.selection.setRanges( ranges );
} );
editor.execute( commandOrCallback, { batch, forceValue: true } );
editor.document.enqueueChanges( () => {
editor.document.selection.setTo( initialSelection );
} ); To again understand that this is wrong because it will render the content 3 times, while I want the selection changes to stay virtual. So, the solution seems to be: editor.document.enqueueChanges( () => {
editor.document.selection.setRanges( ranges );
editor.execute( commandOrCallback, { batch, forceValue: true } );
editor.document.enqueueChanges( () => {
editor.document.selection.setTo( initialSelection );
initialSelection.destroy();
} );
} ); Which even makes sense if you think about it but it's far from straightforward. |
This is faaaar from straightforward and I don't see how it makes more sense than other things that you've tried (even after thinking about it). We should research how we can make nqChanges better. BTW. nqChanges in nqChanges is very confusing and leads to flow problems too. This is was one of the reasons why we did a refactor in |
I know that it's bad. But it makes some sense that in order to prevent rendering between enqChanges you wrap them in another one. Unfortunately, how the rest of the code works (with that one more enqChanges inside) is just terrible. I totally agree. Perhaps we should just give up this idea and remove this function completely. We need to understand whether we have any other option to ensure immutability during one's algorithm execution or not. |
I was thinking about it for the while. The problem is not the We need to have an event which tells that all changes in the model are done and the rendering can start. This event should be fired when the user calls any API method, like At the same time, we want to be able to do something after such method, in an intuitive way, but have the "changes done" event postponed. We could have simple At the same time, there was an idea that new changes should go after all already enqueued change. Despite that, I sill can find cases for it (postfixers), these are edge cases. The default behavior should be to postpone only the editor.document.changes( () => { //these changes are not enqueued anymore
editor.document.selection.setRanges( ranges );
editor.execute( commandOrCallback, { batch, forceValue: true } );
editor.document.selection.setTo( initialSelection );
initialSelection.destroy();
} ); function foo() {
editor.document.changes( () => {
console.log( 'foo' );
} );
}
function bar() {
editor.document.changes( () => {
console.log( 'bar1' );
foo();
console.log( 'bar2');
} );
}
editor.on( 'changesDone', () => console.log( 'changesDone' );
foo();
// foo
// changesDone
bar();
// bar1
// foo
// bar2
// changesDone At the same time it should be possible to enqueue changes: editor.document.changes( () => {
console.log( 'bar1' );
editor.document.changes( () => console.log( 'postfixer' ), { enqueue: true } );
console.log( 'bar2');
} );
// bar1
// bar2
// postfixer
// changesDone In fact, it could be done by the listener in the TL;DR: |
TBH, it was hard to read the code now (I didn't notice the But more importantly, if we're talking about making I'd look for a solution which is plain simple for the 99% of cases and just think on how to implement the remaining 1% and what are its requirements (what use cases precisely we want to support). For example, I can easily imagine that one could simply block |
Please remember, that the first and most important problem that |
On the occasion of #506 I've been thinking about the API a bit and perhaps I'm completely wrong here but I want to write this down just in case I forget. We introduced Anyway, we see right now that it doesn't make any sense to complicate >90% of the code just to solve some edgy cases. So we agree that whatever API we'll come up with should execute the code linearly. However, we may still consider securing us a bit. Any code which does changes will create some kind of transaction (either as At the same time, you may expect this to happen (e.g. when executing one command inside another) and then you can explicitly tell that by e.g. Since we'll be doing post-fixers using the |
No, it's not. The main task is to tell when all changes are done, so, for instance, |
We might use something different for this (something simpler, one method to trigger renderer) so it is not the most important reason, but I already fixed my answer :). |
This is not true at the moment. In fact, it is completely opposite.
I don't like additional flags, etc. Let's make it simple, shall we? |
I know it's not true ATM, but it may be true one day, right? :)
Yeah, that what my post lead to – we don't need that flag if we do all postfixing on |
This topic is bigger, but I wanted to leave here one note. I agree that we can replace synchronous |
Reading https://github.com/ckeditor/ckeditor5-engine/issues/1148 I had a thought that instead of However, I'm not sure, if it's not too abstract to make changes in the "render" callback. Also, I'm not sure on what level it should be then. |
It is ;D
Let's not overthink this. You do/apply changes to the model. That's it. No firing, no rendering. The only other terms which came to my mind and which fit here are those related to transactions (such as "commit"). |
Well, not exactly. Reading https://github.com/ckeditor/ckeditor5-engine/issues/1148#issuecomment-332213851 I realized that this should not be limited to the model. Changes in the view should also be enqueued. |
Still, we should not be talking about rendering or firing stuff. I meant only that. Note that terms such as committing or applying changes are fine in both the model and the view. |
Let's image:
You expect that
someRanges
will be bolded and selection will be restored insomewhere
, right?Wrong!
This code will be executed in this order:
The solution here is to use 2
enqueueChanges()
blocks for selection changes and leave bolding unwrapped (cause it executes its ownenqueueChanges()
). Unfortunately, this is super confusing ;/The text was updated successfully, but these errors were encountered: