how to wait using observables in angular - asynchronous

I need to wait for an API service call to be completed before moving onto the next line of code without using promises async await. How do I accomplish this requirement using observables in Angular 10? Any help is greatly appreciated. Here's the code snippet -
getAll(isSearchComponent: boolean) {
this.abcService.getAll(isSearchComponent)
.subscribe((data) => {
this.aList = data;
}, error => {
this.messageService.add({
severity: 'error', summary: 'Unexpected Error Occured',
detail: 'An error occurred.'
});
});
}
The method getAll in abcService is implemented as shown below -
getAll(includeAll: boolean) {
const opts = this.getOptsApplication();
return this.http.get<Test[]>(environment.baseUrl + `test1/${includeAll}`, opts);
}
PS: Using the Rxjs 6.5.5 version

Related

Apollo Network (Bad Request) error not caught in apollo-link-error

Sending a mutation with a bad set of variables (on purpose in this case) results in errors being thrown in the console, and the apollo-link-error link not picking up the error. Additionally, the mutation loading state is stuck as 'loading' and not error object comes through.
Through a debugging session, I fount that the zen-observable global error handling picks up an error thrown in the error-link's "next" function, because 'result' is not defined
pasted the apollo-link-error code that has the observable wrapped in a try catch, but the catch at the bottom here is not the catch that gets hit when if (result.errors) throws a 'nullpointer' because result in undefined.
try {
sub = forward(operation).subscribe({
next: function (result) {
// result is undefined, throwing an error
if (result.errors) {
retriedResult = errorHandler({
graphQLErrors: result.errors,
response: result,
operation: operation,
forward: forward,
});
if (retriedResult) {
retriedSub = retriedResult.subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
return;
}
}
observer.next(result);
},
error: function (networkError) {
retriedResult = errorHandler({
operation: operation,
networkError: networkError,
graphQLErrors: networkError &&
networkError.result &&
networkError.result.errors,
forward: forward,
});
if (retriedResult) {
retriedSub = retriedResult.subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
return;
}
observer.error(networkError);
},
complete: function () {
if (!retriedResult) {
observer.complete.bind(observer)();
}
},
});
} // the error is NOT caught here
catch (e) {
errorHandler({ networkError: e, operation: operation, forward: forward });
observer.error(e);
}
```
Link definition:
```javascript
export const client = new ApolloClient({
link: ApolloLink.from([
onError((errors) => {
console.log('errors in link!', errors);
handleServerError(errors);
}),
new MeteorAccountsLink(),
new HttpLink({
uri: '/graphql',
}),
]),
cache: new InMemoryCache(),
});
Edit:
The request in the browser does show a response with an error object with the graphQlErro of shape {errors: [{..}]} which is strange that it's not coming into the 'result' in the link.
Edit 2:
It looks like Meteor is picking up the error thrown in the http link prior to the error posted above, which might be why "result" is undefined. Writing a custom link to polyfill the missing 'result' so the app will at least work.
It seems like an issue with Meteor swallowing the errors. fixed make making a polyfill link to at least not break the js in the mutation complete function and show a general error.
const stupidMeteorErorrPolyfill = new ApolloLink((operation, forward) => forward(operation).map(data => data || { errors: [{ message: '! Empty response in gql links, see graphql.js and network tab' }] }));

How to make Polymer 2.x Function async

I am trying to use the Shape Detection API (https://developers.google.com/web/updates/2019/01/shape-detection) and am getting an error:
Uncaught SyntaxError: await is only valid in async function
After going through the Polymer 2.x docs (https://polymer-library.polymer-project.org/2.0/api/namespaces/Polymer.Async) I get the following:
ready() {
super.ready();
this.initImageDetection();
}
initImageDetection() {
const barcodeDetector = new BarcodeDetector({
formats: [
'code_128'
]
});
try {
const barcodes = await barcodeDetector.detect(image);
barcodes.forEach(barcode => console.log(barcode));
} catch (e) {
console.error('Barcode detection failed:', e);
}
}
This pattern also failed with the same error:
this.async(() => {
const barcodes = await barcodeDetector.detect(image)
barcodes.forEach(barcode => console.log(barcode)
)});
Also, running initImageDetection prefixed with async and running from a paper-button after the DOM is loaded.
async initImageDetection() {
...
}
I get the following error:
Uncaught (in promise) ReferenceError: BarcodeDetector is not defined
How do I properly make a function async in Polymer 2.x?
How can I instantiate BarcodeDetector in Polymer 2.x?
async functionName() {
// function code here
}
Is the proper way to set async functions in Polymer. However, the BarcodeDetector object is hidden behind a flag in Chrome, so must be enabled in chrome://flags Experimental Web Platform features before using.

Where is the best place to modify backend response with redux-saga?

I have a function that prepares the errors from backend to be easy for displaying in the components - it's named prepareErrorMessages. It accepts the response from the backend and some default error message.
So - in the saga I have this:
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS
});
}
} catch (err) {
yield put({
type: UPDATE_FAILURE,
payload: prepareErrorMessages(err, 'Failed to update. Please, try again.')
});
}
}
So - am I wrong to modify the errors from the backend here?
Or is it better to do this in the reducer?
case UPDATE_FAILURE:
nextState = {
...state,
loading: false,
errors: prepareErrorMessages(payload, 'Failed to update. Please, try again.'),
};
break;
And also - why is it better to update there?
Personally, I think its right to do it in the reducer.
That is where you handle the responses. Action creators should only set the payload which could be some static data or a promise.
Dont see why you cannot transform/modify the received data there.
Personally, I would prefer to have it in the saga because I think it is the right place of handling this kind of logic.
I prefer my reducers to only be responsible for changing state, not for data transformation.
But it is my personal opinion.
We are using a Transformer for transforming the response getting from the Api. Transformer is the function which takes the input and provide the desired output. Designing the transformer makes the code clean and easy to test.
For example :-
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
// after getting the response from the api pass through the transformer.
const transformedResponse =apiTransformer(action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS,
data: trasnformedResponse
});
}
} catch (error) {
yield put({
type: UPDATE_FAILURE,
error: error)
});
}
}
const apiTransformer = function(apiResponse) {
// implement the logic. This function returns the transformed Response
}
Using this you can move reducer free from the error. Makes the code testable and making mocks easy.
For global backend Errors make a global errorHandler using Redux Middleware, like this
const errorTracking = store => next => action => {
if (/_FAILURE$/.test(action.type)) {
const errorCode = parseInt(
_.get(action, ['error', 'response', 'status'])
)
// this was for my use case
if (errorCode === 403) {
// launch an Global error handler action
return next(ErrorHandlerAction())
} else return next(action)
}
return next(action)
}
While for not genric error You can implement HOC wrap it around the component for visualisation. Thus you can have global implementation for the errors.

Mongoose asynchronous .save and callback

This is one of those problem that you can explain but do not know how to fix it. I have a simple store method:
exports.store = async (req, res) => {
const mydata = new MyModel(req.body)
await mydata.save(function(err,user) {
res.location('/mydata/id/' + user._id)
})
res.status(201).json({ data: userdata })
}
When it runs, I get the following error:
events.js:182
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:489:11)
at ServerResponse.setHeader (_http_outgoing.js:496:3)
at ServerResponse.header (.../node_modules/express/lib/response.js:730:10)
at ServerResponse.location (.../node_modules/express/lib/response.js:847:15)
at .../src/controllers/users-controller.js:26:13
at .../node_modules/mongoose/lib/model.js:3919:16
at .../node_modules/mongoose/lib/services/model/applyHooks.js:162:20
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
Process finished with exit code 1
I appears that the callback function runs separately and asynchronously because the res.status(201).json({ data: userdata }) seems to be producing the error and does not let me set the location header.
I've looked around for how to set the location header but none of the answers are like what I'm trying to do. This seems like something that should have been done before...? I'm looking for some help on how to do this...
You are mixing up two way of thinking :
Promises (with async/await in your case)
Callback
Use only one
try {
const user = await mydata.save();
res.location(`/mydata/id/${user._id}`);
// Other stuff ...
} catch(err) {
// Handle the errors
}
here you get an article about Promises into mongoose.

Meteor 1.3 + React: detect subscription failure?

I have a simple Meteor subscription, and I display a loading message while the data is being loaded. But I don't know how to display error message if subscription failed.
export const MyAwesomeComponent = createContainer(() => {
let sub = Meteor.subscribe('some-data');
if (!sub.ready()) return { message: 'Loading...'};
if (sub.failed()) return { message: 'Failed.' }; // How to do this?
return {
data: Data.find().fetch()
}
}, MyInternalRenderComponent);
Problem is, the subscription object doesn't have a failed() method, only a ready() query. How to pass the failure of a subscription as props in a createContainer() method?
I know the Meteor.subscribe method has an onStop callback for this case, but I don't know how to glue it toghether that to pass a property.
After a lot of researching I managed to get this working and I think it answers your question.
Bear in mind I'm using Meteor 1.6, but it should give you the info to get it working on your side.
On the publication/publish:
try {
// get the data and add it to the publication
...
self.ready();
} catch (exception) {
logger.error(exception);
// send the exception to the client through the publication
this.error(new Meteor.Error('500', 'Error getting data from API', exception));
}
On the UI Component:
const errorFromApi = new ReactiveVar();
export default withTracker(({ match }) => {
const companyId = match.params._id;
let subscription;
if (!errorFromApi.get()) {
subscription = Meteor.subscribe('company.view', companyId, {
onStop: function (e) {
errorFromApi.set(e);
}
});
} else {
subscription = {
ready: () => {
return false;
}
};
}
return {
loading: !subscription.ready(),
company: Companies.findOne(companyId),
error: errorFromApi.get()
};
})(CompanyView);
From here all you need to do is get the error prop and render the component as desired.
This is the structure of the error prop (received on the onStop callback from subscribe):
{
error: String,
reason: String,
details: String
}
[Edit]
The reason there is a conditional around Meteor.subscribe() is to avoid an annoying infinite loop you'd get from the natural withTracker() updates, which would cause new subscriptions / new errors from the publication and so on.

Resources