Subscribe showing deprecated - rxjs7

this.userService.addUser(this.user).subscribe(
(data)=>{
//success
console.log(data);
this._snack.open('Registered Successfully', 'OK', {
duration: 2000,
verticalPosition: 'top',
horizontalPosition: 'center',
panelClass: ['green-snackbar', 'login-snackbar'],
});
},
(error)=>{
//error
console.log(error);
this._snack.open('Something went Wrong', 'OK', {
duration: 2000,
verticalPosition: 'top',
horizontalPosition: 'center',
panelClass: ['red-snackbar','login-snackbar'],
});
}
);
I don't know what exactly is happening but subscribe showing deprecated, following is the message
(method) Observable<Object>.subscribe(next?: ((value: Object) => void) | null | undefined, error?: ((error: any) => void) | null | undefined, complete?: (() => void) | null | undefined): Subscription (+2 overloads)
#deprecated — Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments
'(next?: ((value: Object) => void) | null | undefined, error?: ((error: any) => void) | null | undefined, complete?: (() => void) | null | undefined): Subscription' is deprecated.ts(6385)
Observable.d.ts(55, 9): The declaration was marked as deprecated here.
By the way I am using Angular13

Just change it to:
this.userService.addUser(this.user).subscribe({
next: (data) => {
console.log(data);
this._snack.open('Registered Successfully', 'OK', {
duration: 2000,
verticalPosition: 'top',
horizontalPosition: 'center',
panelClass: ['green-snackbar', 'login-snackbar'],
});
},
error: (error) => {
console.log(error);
this._snack.open('Something went Wrong', 'OK', {
duration: 2000,
verticalPosition: 'top',
horizontalPosition: 'center',
panelClass: ['red-snackbar','login-snackbar'],
});
}
});

Related

AlpineJS can not use magic method $watch

Having the following AlpineJS code and trying to use the magic method $watch, the code will fail with ReferenceError: $watch is not defined
window.experts = {
apiUrl: 'http://test.local:8991/api/',
data: [],
list: [],
expertsForm: null,
expertType: 'all',
queryUrl: '',
currentPage: 1,
sortByName: 'asc',
sortByZip: 'asc',
q: '',
fetchStatus: 'loading...',
retrieveList: () => {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
$watch('specialistType', (value) => console.log(value) );
experts.apiCalls(membersUrl)
},
setExpertType: (type) => {
console.log(type)
},
apiCalls: (url) => {
const response = fetch(url).then(res => {
if (!res.ok) {
experts.fetchStatus = 'error'
}
return res.json()
}).then(result => {
experts.list = result.data;
experts.data = result;
experts.fetchStatus = 'idle'
});
}
}
what goes wrong in this case?
Try accessing it via this. So it should be this.$watch(value, callback).
You should not use arrow function and add this
retrieveList(){
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
this.$watch('specialistType', (value) => console.log(value) );
experts.apiCalls(membersUrl)
},

Why Am I getting an object with action observer property from selector using withLatestFrom operation in effect?

The "res" value is an object, that is not retrieving the data related to the selector, is working in other places, but in the effect is getting this object. Why is happening this?
constructor(
private serviceStore: Store<DataState>,
) {
searchForLatest$ = createEffect(() =>
this._actions.pipe(
ofType<GetLatestRequestService>(GetLatestData),
withLatestFrom(({ id }) =>
this.serviceStore.select(getlatestData(id)),
mergeMap(res => {
actionsObserver: {
closed: false,
hasError: false,
isStopped: false,
observers: [SkipSubscriber],
thrownError: null,
_isScalar: false,
}
operator: {
compare: undefined
keySelector: undefined
}
reducerManager: {
closed: false
dispatcher: DevtoolsDispatcher {_isScalar: false, observers: Array(1), closed: false,
isStopped: false, hasError: false, …}
hasError: false
initialState: undefined
isStopped: false
observers: [MapSubscriber]
reducerFactory: (reducers, initialState) => {…}
reducers: {uiContext: ƒ, parties: ƒ, user: ƒ, organizationsDetail: ƒ, activeRoute: ƒ, …}
thrownError: null
_isScalar: false
_value: (state, action) =>
}
Source: {
actionsObserver: ActionsSubject {_isScalar: false, observers: Array(1), closed: false,
isStopped: false, hasError: false, …}
operator: MapOperator {thisArg: undefined, project: ƒ}
reducerManager: ReducerManager {_isScalar: false, observers: Array(1), closed: false,
isStopped: false, hasError: false, …}
source: Store {_isScalar: false, actionsObserver: ActionsSubject, reducerManager:
ReducerManager, source: Observable}
_isScalar: false
}
_isScalar: false
The effects in v13 updated the approach to retrieve the latest data from a selector, I need to use the concatLatestFrom operator to get the data.
#Injectable()
export class CollectionEffects {
addBookToCollectionSuccess$ = createEffect(
() =>
this.actions$.pipe(
ofType(CollectionApiActions.addBookSuccess),
concatLatestFrom(action => this.store.select(fromBooks.getCollectionBookIds)),
tap(([action, bookCollection]) => {
if (bookCollection.length === 1) {
window.alert('Congrats on adding your first book!');
} else {
window.alert('You have added book number ' + bookCollection.length);
}
})
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private store: Store<fromBooks.State>
) {}
}
Note: For performance reasons, use a flattening operator like concatLatestFrom to prevent the selector from firing until the correct action is dispatched.

Dispatch actions in order within effect

I am unable to get two actions to execute sequentially within a NGRX effect.
Here are the effects
registerFinishSave$ = createEffect(
() => this.actions$.pipe(
ofType(registerFinishSave),
withLatestFrom(
this.store.select(selectRouteState),
(action, state) => ({
firstName: action.request.firstName,
id: state.queryParams.id,
lastName: action.request.lastName,
password: action.request.password,
repeatPassword: action.request.repeatPassword,
token: state.queryParams.token,
userName: action.request.userName
})),
switchMap((request) =>
this.accountService.updateAndVerifyAccount(request).pipe(
mergeMap((response: any) => from([registerFinishSaveSuccess({ response }), loginSuccess({response})])),
// map((response: any) => registerFinishSaveSuccess({ response })),
catchError((response: HttpErrorResponse) => {
if (response.status === 400) {
return of(registerFinishSaveFailure({ response }));
}
throw response;
})
)
)
)
);
registerFinishSaveSuccess$ = createEffect(
() => this.actions$.pipe(
ofType(registerFinishSaveSuccess),
tap(({ response }) => {
console.log('registerFinishSaveSuccess ');
// this.router.navigate(['/']);
this.notificationService.info(response.message);
})
), {
dispatch: false
}
);
loginSuccess$ = createEffect(
() => this.actions$.pipe(
ofType(loginSuccess),
tap(() => {
console.log('loginSuccess ');
return this.router.navigate(['/']);
})
), {
dispatch: false
}
);
Here are the reducers
on<AuthenticationState>(loginSuccess, (state: AuthenticationState, { response }) => ({
...state, accessToken: response.accessToken, displayName: response.displayName, id: response.id, isAuthenticated: true, refreshToken: response.refreshToken
})),
on<AccountState>(registerFinishSaveSuccess, (state: AccountState, { response }) => ({
...state, message: null
})),
The action and effect for registerFinishSaveSuccess appears to work. The reducer appears to set the state for the action loginSuccess. However the effect loginSuccess does not appear to fire.
I need to fire the registerFinishSaveSuccess and loginSuccess actions and their effects sequentially. Not sure if the code below is correct
'''
mergeMap((response: any) => from([registerFinishSaveSuccess({ response }), loginSuccess({response})])),
'''
There is no need to dispatch both in a mergeMap. You can dispatch your registerFinishSaveSuccess and then his effect would be triggered. In the registerFinishSaveSuccess effect, you can dispatch loginSuccess and that way you would be sure the order is kept.

Moxios Requests State Not Cleared In Between Tests

My specs are behaving weirdly in that when I run the tests alone, they pass. However, when I run the test suite all together, the failure tests still continue to use the success axios mock instead of using the correct failing http axios mock. This results in my tests failing. Am I missing something for isolating the 2 mocks from each other in the different portions of code?
jobactions.js
export const loadUnassignedJobs = (job_type) => {
if (!['unscheduled', 'overdue'].includes(job_type)) {
throw 'Job Type must be "unscheduled" or "overdue".';
}
return (dispatch) => {
dispatch({type: JobActionTypes.LOAD_UNASSIGNED_JOBS_STARTED, job_type });
return axios.get(defaults.baseapi_uri + 'jobs/' + job_type)
.then(function (response) {
dispatch(updateUnassignedJobs(response.data.jobs));
// handle success
})
.catch(function (error) {
// handle error
dispatch({ type: JobActionTypes.LOAD_UNASSIGNED_JOBS_FAILURE, error });
})
.then(function () {
// always executed
});
}
};
export const updateUnassignedJobs = (unassigned_jobs) => {
let unassigned_job_ids = [];
let jobs = {};
for (let job of unassigned_jobs) {
unassigned_job_ids.push(job.id);
jobs[job.id]=job;
}
return({
type: JobActionTypes.LOAD_UNASSIGNED_JOBS_SUCCESS,
jobs,
unassigned_job_ids,
});
};
spec.js
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import * as jobActions from "../../../app/javascript/actions/JobActions"
import { findAction } from '../support/redux_store'
import * as JobActionTypes from '../../../app/javascript/constants/JobActionTypes'
import fixtures_jobs_unscheduled_success from '../fixtures/jobs_unscheduled_success'
import moxios from "moxios";
export const mockStore = configureMockStore([thunk]);
let store;
describe ('loadUnassignedJobs', () => {
context('when bad parameters are passed', async () => {
it('will raise an error', () => {
const store = mockStore();
expect(() => {
store.dispatch(jobActions.loadUnassignedJobs('wrong_type'));
}).to.throw('Job Type must be "unscheduled" or "overdue".');
});
});
context('when unscheduled is passed', () => {
beforeEach(() => {
moxios.install();
console.log("before each called");
console.log(moxios.requests);
store = mockStore();
store.clearActions();
});
afterEach(() => {
console.log("after each called");
console.log(moxios.requests);
moxios.uninstall();
});
context('on success', () => {
beforeEach(() => {
moxios.wait(() => {
let request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: fixtures_jobs_unscheduled_success
});
});
})
it('dispatches LOAD_UNASSIGNED_JOBS_STARTED', () => {
store.dispatch(jobActions.loadUnassignedJobs('unscheduled')).then(() => {
expect(findAction(store, JobActionTypes.LOAD_UNASSIGNED_JOBS_STARTED)).to.be.eql({
type: JobActionTypes.LOAD_UNASSIGNED_JOBS_STARTED,
job_type: 'unscheduled'
});
});
});
it('dispatches updateUnassignedJobs()', () => {
store.dispatch(jobActions.loadUnassignedJobs('unscheduled')).then(() => {
expect(findAction(store,JobActionTypes.LOAD_UNASSIGNED_JOBS_SUCCESS)).to.be.eql(jobActions.updateUnassignedJobs(fixtures_jobs_unscheduled_success.jobs))
});
});
});
context('on error', () => {
beforeEach(() => {
//console.log("before each on error called");
//console.log(moxios.requests);
moxios.wait(() => {
console.log('after waiting for moxios..')
console.log(moxios.requests);
let request = moxios.requests.mostRecent();
request.respondWith({
status: 500,
response: { error: 'internal server error' }
});
});
})
it('dispatches LOAD_UNASSIGNED_JOBS_FAILURE', (done) => {
console.log(moxios.requests);
store.dispatch(jobActions.loadUnassignedJobs('unscheduled')).then(() => {
console.log(moxios.requests);
console.log(store.getActions());
expect(findAction(store, JobActionTypes.LOAD_UNASSIGNED_JOBS_FAILURE)).to.include({
type: JobActionTypes.LOAD_UNASSIGNED_JOBS_FAILURE
});
expect(findAction(store, JobActionTypes.LOAD_UNASSIGNED_JOBS_FAILURE).error).to.include({
message: 'Request failed with status code 500'
});
done();
});
});
it('does not dispatch LOAD_UNASSIGNED_JOBS_SUCCESS', (done) => {
store.dispatch(jobActions.loadUnassignedJobs('unscheduled')).then(() => {
expect(findAction(store, JobActionTypes.LOAD_UNASSIGNED_JOBS_SUCCESS)).to.be.undefined;
done();
});
});
})
});
});
describe('updateUnassignedJobs', () => {
it('assigns jobs to hash and creates an unassigned_job_ids array', () => {
expect(jobActions.updateUnassignedJobs([ { id: 1, step_status: 'all_complete' }, { id: 2, step_status: 'not_started' } ])).to.be.eql(
{
type: JobActionTypes.LOAD_UNASSIGNED_JOBS_SUCCESS,
jobs: { 1: { id: 1, step_status: 'all_complete' }, 2: { id: 2, step_status: 'not_started' } },
unassigned_job_ids: [ 1,2 ]
}
)
});
});
Found the issue!
The it() blocks for the success case were not using the done callback causing the afterEach() moxios.uninstall() to be called prematurely and not resetting the requests after the call was complete. Fixing this, and now all the tests pass.

ckeditor with placeholder plguin enhancement double click issue

I need a placeholder/variable that takes name, defaultValue, tooltip/description. I created a plugin and it is working fine in the editor/create mode. When placeholder is created, it is adding the following tags to source
<var class="cke_placeholder" name="varName" title="varToolTip">[[varDefaultValue]]</var>
Image that depicts create & edit mode differences
When I save the html content with placehoder in db and trying to load it back to ckeditor, I am not able to get the + symbol and hence not able to launch the editor.
Here is my ckeditor/plugins/var/plguin.js
'use strict';
( function() {
CKEDITOR.plugins.add( 'var', {
requires: 'widget,dialog',
icons: 'var', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
onLoad: function() {
CKEDITOR.dialog.add( 'var', this.path + 'dialogs/var.js' );
},
init: function( editor ) {
this.registerWidget( editor );
editor.ui.addButton && editor.ui.addButton( 'Var', {
label: 'Create Variable',
command: 'var',
toolbar: 'insert',
icon: 'var'
} );
},
registerWidget: function(editor){
var that = this;
// Put ur init code here.
editor.widgets.add( 'var', {
// Widget code.
dialog: 'var',
pathName: 'var',
// We need to have wrapping element, otherwise there are issues in
// add dialog.
template: '<var class="cke_placeholder">[[]]</var>',
downcast: function() {
return new CKEDITOR.htmlParser.text( '<var class="cke_placeholder" name="'+this.data.name+'" title="'+this.data.description+'">[[' + this.data.defaultValue + ']]</var>' );
},
init: function() {
this.setData( 'defaultValue', this.element.getText().slice( 2, -2 ) );
this.setData( 'name', this.element.getAttribute("name") );
this.setData( 'description', this.element.getAttribute("title") );
},
data: function() {
this.element.setText( '[[' + this.data.defaultValue + ']]' );
this.element.setAttribute('name', this.data.name );
this.element.setAttribute('title', this.data.description );
}
} );
},
afterInit: function( editor ) {
this.registerWidget( editor );
/*var placeholderReplaceRegex = /\[\[([^\[\]])+\]\]/g;
editor.dataProcessor.dataFilter.addRules( {
text: function( text, node ) {
var dtd = node.parent && CKEDITOR.dtd[ node.parent.name ];
// Skip the case when placeholder is in elements like <title> or <textarea>
// but upcast placeholder in custom elements (no DTD).
if ( dtd && !dtd.span )
return;
return text.replace( placeholderReplaceRegex, function( match ) {
// Creating widget code.
var widgetWrapper = null,
innerElement = new CKEDITOR.htmlParser.element( 'span', {
'class': 'cke_placeholder'
} );
// Adds placeholder identifier as innertext.
innerElement.add( new CKEDITOR.htmlParser.text( match ) );
widgetWrapper = editor.widgets.wrapElement( innerElement, 'placeholder' );
// Return outerhtml of widget wrapper so it will be placed
// as replacement.
return widgetWrapper.getOuterHtml();
} );
}
} );*/
}
} );
} )();
Here is my ckeditor/plugins/var/dialogs/var.js
'use strict';
CKEDITOR.dialog.add( 'var', function( editor ) {
//var lang = editor.lang.var,
//generalLabel = editor.lang.common.generalTab,
var generalLabel = 'General',
validRegex = /^[^\[\]<>]+$/,
emptyOrInvalid = ' can not be empty. It can not contain any of following characters: [, ], <, >',
invalid = ' can not contain any of following characters: [, ], <, >';
return {
title: 'Variable properties',
minWidth: 300,
minHeight: 80,
contents: [
{
id: 'info',
label: generalLabel,
title: generalLabel,
elements: [
// Dialog window UI elements.
{
id: 'name',
type: 'text',
style: 'width: 100%;',
label: 'Name',
'default': '',
required: true,
validate: CKEDITOR.dialog.validate.regex( validRegex, 'name'+emptyOrInvalid ),
setup: function( widget ) {
this.setValue( widget.data.name );
},
commit: function( widget ) {
widget.setData( 'name', this.getValue() );
}
},
{
id: 'defaultValue',
type: 'text',
style: 'width: 100%;',
label: 'Default Value',
'default': '',
required: true,
validate: CKEDITOR.dialog.validate.regex( validRegex, 'Default Value'+emptyOrInvalid ),
setup: function( widget ) {
this.setValue( widget.data.defaultValue );
},
commit: function( widget ) {
widget.setData( 'defaultValue', this.getValue() );
}
},
{
id: 'description',
type: 'text',
style: 'width: 100%;',
label: 'Description',
'default': '',
required: true,
validate: CKEDITOR.dialog.validate.regex( validRegex, 'Description'+invalid ),
setup: function( widget ) {
this.setValue( widget.data.description );
},
commit: function( widget ) {
widget.setData( 'description', this.getValue() );
}
}
]
}
]
};
} );

Resources