How to destroy a Ckeditor 5 instance - ckeditor5

How can I unload a Ckeditor 5 instance from a form object? I can load it with ClassicEditor.create(). I found the editor.destroy() method but it does not work. The Javascript console says "editor.destroy is not a function".
Current testing code is:
<button type="button" onclick="ckEditor('load')">Start</button><button type="button" onclick="ckEditor('unload')">Stop</button>
<textarea id="welcomeText" class="form-control" tabindex="21" name="txt_welcomeText" rows="10"><p>This is my welcome text.</textarea>
<script>
function ckEditor(action) {
editor = ClassicEditor.create( document.querySelector( '#welcomeText' ) ).catch( error => {console.error( error );});
if (action == "unload") editor.destroy();
}
</script>
Best regards,
George

There's a need to wait for the promise in order to get the CKEditor 5 editor's instance.
ClassicEditor.create( element )
.then( editor => editor.destroy() )
.catch( err => console.error( err ) )

this worked for me :
document.querySelector('.ck-editor__editable').ckeditorInstance.destroy()
All you have to do is getting the instance , then destroying it !
.ck-editor__editable is the default class for the CKeditor.
You can find more explanation in the official documentation : https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/basic-api.html#destroying-the-editor

Related

handle errors with HTMX

<form
class="" id="form" hx-post="/add/" hx-swap="afterbegin" hx-target="#big_list" hx-trigger="submit">
<input type="text" name="langue1" >
<input type="text" name="langue2">
<div id="errors"></div>
<button type="submit">GO</button>
</form>
<div id="big_list">
.....
</div>
I have a big list in #big_list, and I want my #form appends only one row when submitted.
How with htmx, can I handle errors and show message in #errors ?
I created this solution so you can use hx-target-error = to define which HTML will be displayed after a failed request
document.body.addEventListener('htmx:afterRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (evt.detail.failed && targetError) {
document.getElementById(targetError.value).style.display = "inline";
}
});
document.body.addEventListener('htmx:beforeRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (targetError) {
document.getElementById(targetError.value).style.display = "none";
}
});
If your code raises the errors (validation?), you can change target and swap behavior with response headers.
Response.Headers.Add("HX-Retarget", "#errors");
Response.Headers.Add("HX-Reswap", "innerHTML");
If you want to return a status other than 200, you have to tell htmx to accept it.
4xx would normally not do a swap in htmx. In case of validation errors you could use 422.
document.body.addEventListener('htmx:beforeOnLoad', function (evt) {
if (evt.detail.xhr.status === 422) {
evt.detail.shouldSwap = true;
evt.detail.isError = false;
}
});
It works in htmx 1.8.
If you want to remove the error message on then next sucessfull request, you could use hx-swap-oob. Out of band elements must be in the top level of the response.
So the response could look like this:
<div>
your new row data...
</div>
<div id="errors" hx-swap-oob="true"></div>
Update
You can now use the new powerful extension multi-swap to swap multiple elements arbitrarily placed and nested in the DOM tree.
See https://htmx.org/extensions/multi-swap/
Although it doesn't follow REST principles, you might consider using an swap-oob to report your error back to your user. For example, your request might return a (slightly misleading) status 200, but include content like this:
<div id="errors" hx-swap-oob="true">
There was an error processing your request...
</div>
If it's important to follow REST more precisely, then you'll want to listen to the htmx:responseError event, as mentioned by #guettli in his previous answer.

VueJS2: Help tracking down "TypeError: Cannot read property 'XXX' of undefined"

I am using VueJS2 and having the following issue when returning API data from a function:
Uncaught (in promise) TypeError: Cannot read property 'P_shortName' of undefined
While the UI does render the data successfully after a while, I get annoying console log output above.
In template I have:
<td>
{{ registerName(country.registerIdentifier_FK) }}
</td>
And the registerName() looks like so:
methods: {
async registerName(id) {
const register = this.registers.find(
register => id === register.registerIdentifier_PK
)
return register.P_shortName
},
}
What can I do to mitigate this error?
Try it.
And I didn't see the need to use async here.
registerName(id) {
const register = this.registers.find(
(register) => id === register.registerIdentifier_PK
);
return register ? register.P_shortName : null;
}

How come is my array not reactive in vuejs?

Good evening everyone,
I have been making a kind of social network as a personal project using vuejs, nodejs and mysql database.
Basically, you can post a message, and then people can answer to it. I bind comments to post using an id. I got two tables: 1 comments and 1 posts. If a comment is posted for post number 38, in mysql table there is a field idPost = 38.
i got a function displaying all the answers for the post by clicking on a button, which is:
displayAnswers(id) {
axios.get('http://localhost:3000/wall/answer/get/'+id )
.then(response => {
this.answers = response.data.resultat;
})
.catch(error => {
console.log(error);
})
}
Where id is the id of the post I want to display answers.
Now, the problem is when I add a comment, I need either to refresh the page to see the comment or to force the refresh by calling the displaypost function, like this:
postAnswer(id) {
let syntaxe = /^[a-z A-ZáàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ0-9-]{1,}$/;
if(syntaxe.test(this.answerToPost)) {
let answer = {
message: this.answerToPost,
postId: id,
auteur: this.$store.state.pseudoUser
}
axios.post('http://localhost:3000/wall/post/answer', answer)
.then(response => {
console.log(response);
this.feedbackMessage = response.data.message;
this.isAlert = false;
this.answerToPost = '';
setTimeout(() => {
this.feedbackMessage = ''
}, 2000);
this.displayAnswers(id);
})
.catch(error => {
console.log(error);
this.feedbackMessage = error.response.data.message;
this.isAlert = true;
})
} else {
this.errorMessage = "Le message ne respecte pas la syntaxe autorisée";
return;
}
},
To summarize, my data this.answers, is not reactive. it is declared this way in the app:
data() {
return {
Auteur: '',
displayPostAnswers: [],
answerToPost: '',
isAlert: true,
feedbackMessage: '',
answers: ''
}
},
and called this way in my template, using a v-for loop to display the answers:
<div v-for="answer in answers" :key="answer.id" class="answerDisplayer" >
<div class="containerEachAnswer">
<div class="avatarAuteur">
<img src="../assets/defaultUser.png" width="48" height="48" alt="">
</div>
<div class="answer">
<span>
<strong>{{ answer.auteur }}</strong><br>
{{ answer.message}}
</span>
</div>
</div>
</div>
</div>
I looked for the issue on the internet, I found this doc: https://fr.vuejs.org/v2/guide/reactivity.html.
So I tried to use the function Vue.set but it does not seem to work.
I would like to know if more experienced developer could help me to find another way to either make my data reactive or help me to do it another way, I tried several kind of things but it did not work.
PS: I tried to use computed data, but v-for does not work with computed data.
Thank you!
Have a good evening!
Since you are trying to change this within the instance I suggest you try this.$set(this.someObject, 'b', 2) as described in https://fr.vuejs.org/v2/guide/reactivity.html#Pour-les-objects
Also you seem to declare answers as a string in your data function, try declaring it as an array answers: []

In Cypress how to found count a selection with same ID and get the length?

I have a such HTML code.
<div id ='pages'>
<div id='wrapper'>1 </div>
<div id='wrapper'>2 </div>
</div>
I am want to find elements count with id wrapper.
I using Cypress. I'm starting to learn Cypress.
If I try:
cy.get('div#wrapper').should('have.length', 2)
I get AssertionError:
CypressError: Timed out retrying: expected 1 to equal 2
As jonrsharpe pointed out, it's invalid HTML to have multiple elements with identical id attribute.
That being said, DOM is quite smart and can recover and work even with invalid HTML. Duplicate-id elements shouldn't cause much trouble.
If you e.g. try doing document.querySelectorAll('#wrapper') it should return list of 2 elements (in your case).
Problem is, Cypress is using jQuery to query the DOM instead of using native DOM methods and I guess jQuery isn't as smart (or it's more pedantic).
That being said, I can't reproduce that error when running:
// succeeds
cy.get('div#wrapper').should('have.length', 2)
Only when querying #wrapper directly (without the preceding div):
// fails
cy.get('#wrapper').should('have.length', 2)
I reckon this is because jQuery uses a heuristic of exiting early when a selector string (#wrapper) contains only a single id (and that's why div#wrapper returns both elements).
Also, your solution in comments (cy.get('#pages') .find('div#wrapper') .should(($div) => { expect($div).to.have.length(2) })), while working, isn't ideal because it won't retry. Let me demonstrate:
In the following code, the 2nd #wrapper will appear in the DOM only after 1 sec.
describe( 'test', () => {
beforeEach(() => {
cy.document().then( doc => {
doc.body.innerHTML = `
<div id='pages'>
<div id='wrapper'>1</div>
</div>
`;
setTimeout(() => {
doc.body.innerHTML = `
<div id='pages'>
<div id='wrapper'>1</div>
<div id='wrapper'>2</div>
</div>
`;
}, 1000 );
});
});
// will fail
it('solution A', () => {
cy.get('#pages') // <- won't be retried
.find('div#wrapper') // <- only this command will be retried
.should( $div => expect($div).to.have.length(2) );
});
// will pass
it('solution B', () => {
cy.get('#pages #wrapper') // <- will be retried and succeed in 1sec
.should( $div => {
expect($div).to.have.length(2);
});
});
// will pass
it('solution C', () => {
cy.get('#pages')
.should($pages => {
// using native DOM querying
expect($pages[0].querySelectorAll('#wrapper').length).to.eq(2);
});
});
});
Thus, you should go with solution similar to B or C.

Error handling with Angular2 async pipe

I am using the Angular2 async pipe to stream values into the DOM. Here's a real simple example:
const stream = Observable.interval(1000)
.take(5)
.map(n => { if (n === 3) throw "ERROR"; return n; });
<div *ngFor="for num of stream | async">
{{num}}
</div>
<div id="error"></div>
What I would like to do is to have the sequence of 1-5 displayed, but on the error item (3), somehow populate the #error div with the error message.
This seems to require two things: first is the ability of the Angular async pipe to do something intelligent with errors, which I see no sign of. Looking at the source code, apparently it throws a JS exception, which doesn't seem too friendly.
Second is the ability to restart or continue the sequence after the error. I have read about catch and onErrorResumeNext and so on, but they all involve another sequence which will be switched to on an error. This greatly complicates the logic of generating the stream, on which I would just like to put a series of numbers (in this simple example). I have the sinking feeling that once an error occurs the game is over and the observable is completed and can only be "restarted" with a different observable. I'm still learning observables; is this in fact the case?
So my question is twofold:
Can Angular2's async pipe do something intelligent with errors?
Do observables have some simple way to continue after an error?
Yes you're right regarding the catch operator and the ability to do something after errors occur...
I would leverage the catch operator to catch the error and do something:
const stream = Observable.interval(1000)
.take(5)
.map(n => {
if (n === 3) {
throw Observable.throw(n);
}
return n;
})
.catch(err => {
this.error = error;
(...)
});
and in the template:
<div>{{error}}</div>
To be able to go on the initial observable, you need to create a new one starting at the point where the error occurs:
createObservable(i) {
return Observable.interval(1000)
.range(i + 1, 5 - i)
.take(5 - i)
});
}
and use it in the catch callback:
.catch(err => {
this.error = error;
return this.createObservable(err);
});
These two questions could help you:
How to resumeOnError (or similar) in RxJS5
RxJS Continue Listening After Ajax Error (last answer)
1) no, The async pipe subscribes and unsubscribes and returns the events it receives. You would need to handle the errors before they receive the async pipe.
2) You can use the catch operator and when it returns an observable then its value(s) is emitted by the .catch(err => Observable.of(-1)) instead of the error.
You could use this to emit a special "error" value and then use something like *ngIf="num === -1 to show the error value in some special way.
You can find more information on this https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html
#Thierry Templier answer was correct but is now a bit outdated. Here's how to do it with the latest RXJS.
this.myObservable$ = this.myService.myFunc().pipe(
catchError(() => of([])) // this will emit [] if the request fails - u could handle this [] emit on error in the service itself
)
then HTML as normal:
<div *ngFor="let xxx of (myObservable$ | async)">
</div>
Note $ at end of Observable name is Angular recommended way to denote an Observable.
I was facing a similar issue and came up with another approach. I do not know if it's a good way of doing it, but it works.
template where you want to show the result of your observable:
<div *ngIf="tableData$ | async as tableData; else loader" class="mt-4">
<!-- do something with tableData -->
</div>
<ng-template #loader>
<loading [target]="tableData$"></loading>
</ng-template>
The loading component:
export class LoadingComponent implements OnInit {
private _errorMessageSubject : Subject<string> = new Subject<string>();
private _errorMessage$ : Observable<string> = this._errorMessageSubject.asObservable();
public get errorMessage$() : Observable<string> { return this._errorMessage$; }
private _target : Observable<any> | null = null;
public get target() : Observable<any> | null { return this._target }
// this input does nothing except catch the error and feed the
// message into the errorMessage subject.
#Input() public set target(o: Observable<any> | null) {
if(o == null) { return; }
this._target = o.pipe(
catchError((error, _) => {
this._errorMessageSubject.next(error);
return of(null);
}),
);
};
constructor() { }
ngOnInit(): void {
}
}
loader template:
<div *ngIf="target && target | async;">
</div>
<div *ngIf="errorMessage$ | async as error; else loading">
<p class="text-danger">{{ error }}</p>
</div>
<ng-template #loading> <!-- simply a spinner icon -->
<div class="d-flex justify-content-center">
<fa-icon [icon]="['fas', 'spinner']" size="6x" [spin]="true"></fa-icon>
</div>
</ng-template>
I am not perfectly sure if its a good approach to subscribe to the observable twice, as subscribing is done in the original component that needs the data and in the loader, but otherwise this seems to work properly.