Angular returning null for params value - javascript

In my Angular app I am having trouble with getting the value from my params and thus data from my API call.
I keep getting null and [object Object] in browser console for my console.log
EDIT: I have noticed that upon loading this page the tag value is overwhritten wit undefined.. image bellow:
Here is the code I am using to get params value and show products with this tag:
EDIT: search.service.ts file where the API is called
searchByTagCall(tag: string) {
return from(Preferences.get({key: 'TOKEN_KEY'})).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
let params = new HttpParams();
params = params.append('tag', tag);
return this.httpClient.get(`${environment.apiUrl}search`, {headers, observe: 'response', params});
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', {replaceUrl: true});
}
return EMPTY;
}),
);
}
EDIT: home.page.ts Code where I click the tag that redirects to the page where products with this tag should be shown:
searchByTag(tag: string) {
this.tagsSubscription = this.searchService.searchByTagCall(tag).subscribe((data: any) => {
this.searchService.tag = data;
this.router.navigate(['/tag-search/', tag]);
},
error => {
console.log('Error', error);
});
}
and html:
<ion-chip *ngFor="let tag of tags">
<ion-label class="tag" (click)="searchByTag(tag.tags)">{{ tag.tags }}</ion-label>
</ion-chip>
tag-search.page.ts:
export class TagSearchPage implements OnInit {
tag: string;
products: any = [];
constructor(
private route: ActivatedRoute,
private searchService: SearchService,
) { }
ngOnInit() {
this.showTagProducts();
}
showTagProducts() {
const tag = String(this.route.snapshot.paramMap.get('tag'));
this.searchService.searchByTagCall(tag).subscribe(
(data: any) => {
console.log('Products with tag: ' + tag + ' ' + data);
},
error => {
console.log('Error', error);
});
}
}
Here is how my JSON response looks like:
[
{
"id": 1283,
"name": "Some product name",
"product_code": "470631"
},
{
"id": 786,
"name": "A different product name",
"product_code": "460263"
}
]

Related

Vue how to display response from backend API

Sorry for such noob question but I'm a simply gave up. I've got below code which creates a POST request to the BE part of the app - user provides input (productCodes) and push submit button. That part works well, Vue sends request to BE but in the response FE should have received JSON with result: { id: 1234 }
How do I get this response and display it inside the app? I've got below:
imports.js
const createProductsRequest = (self, products) => {
const jwtToken = self.$store.state.idToken;
const payload = JSON.stringify({ product_codes: products['product_codes'].split(',') })
return axios
.post(`/api/v1/imports/products/`, payload,{
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => response.data)
};
export {
createProductsRequest
};
sync_product.vue
<script>
import {
createProductsRequest
} from '../../api/imports'
import ModalController from '../general/modal_controller'
export default {
name: 'BackboneSyncProducts',
data() {
return {
styleCodes: [],
}
},
computed: {
productsToSyncAmount () {
return this.styleCodes.length
},
},
methods: {
async syncProducts() {
let confirmationText = `Do you want to ${this.productsToSyncAmount} sync products?`
if (this.productsToSyncAmount === 0) {
ModalController.showToast('', 'Type product codes for sync first, please!', 'warning')
}
else if (await ModalController.showConfirmation('Confirmation', confirmationText)) {
try {
ModalController.showLoader()
await createProductsRequest(this, this.styleCodes)
const successMessage = `${this.productsToSyncAmount} products have been queued for sync`
await ModalController.showToast('', successMessage)
} catch (data) {
const errorMessage = `Error occurred during queueing products to sync - `
ModalController.showToast('', errorMessage + data?.message, 'error')
} finally {
this.styleCodes = []
ModalController.hideLoader()
}
}
},
}
}
</script>
That's all what I have.
Instead of calling your request without getting the return value:
await createProductsRequest(this, this.styleCodes)
You can get the return value which is the result of the request :
const data = await createProductsRequest(this, this.styleCodes)
By doing that, data must contain the result of the request, as you mentionned { id: 1234 }.
--
If you want to use this result in your component, you can create a reactive value in data()
data() {
return {
styleCodes: [],
data: null
}
},
And store the result like this :
this.data = await createProductsRequest(this, this.styleCodes)
With that you can display it in your template for example :
<template>
<!-- There is a v-if because before doing the request, data is null -->
<div v-if="data">{{ data.id }}</div>
</template>

Vue.js getting null for the json when initially loading

I am learning vue.js and trying to call a json file and parse the data. Everything is working but when I refresh the page, I see a blank page before my data is loaded and in console log I get TypeError: e.myData is null. After few second the data is displayed on the page.
main.js:
new Vue({
el: `#app`,
render: h => h(App,{
props:{
todoID: this.dataset.id
}
})
})
and my App.vue:
<template>
<div>
<div>{{myData.getName()}}</div>
</div>
</template>
<script>
import Info from '#/getInfo'
export default {
name: 'App',
props: {
todoID: String,
},
data() {
return {
data: null,
myData: null
}
},
created() {
fetch(`http://localhost/json-end-point/`)
.then(async response => {
const data = await response.json();
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
// set the date here, am I doing this right?
this.myData = new Info(data);
this.data = data;
})
.catch(error => {
this.errorMessage = error;
console.error("There was an error!", error);
});
}
}
</script>
getInfo.js
export default class Info {
constructor(Data) {
if (Data) {
const data = JSON.parse(Data);
console.log(data.name); // returns name
// console.log(data); // returns the json data
this.name = data.name;
}
}
getName() {
return this.name;
}
}
I think I am doing something wrong here this.myData = new Info(data); but I am not sure what I can do.
Update
I was able to remove the console log by adding v-if="myData !== null". Still not sure assigning data like this this.myData = new Info(data); is a good approach. How can I improve this code?
Here's an example of how to let the template show different states so it doesn't try to render the data before it arrives, but shows a loading indicator instead.
<template>
<div v-if="myData">
<div>{{ myData.getName() }}</div>
</div>
<div v-else-if="isLoading">
Loading data...
</div>
<div v-else-if="errorMessage">
{{ error.message }}
</div>
</template>
<script>
import Info from '#/getInfo'
export default {
name: 'App',
props: {
todoID: String,
},
data() {
return {
data: null,
myData: null,
error: null
}
},
async created() {
await this.getData()
},
methods: {
async getData() {
try {
const response = await fetch(`http://localhost/json-end-point/`)
if (!response.ok) {
throw new Error(data?.message || response.statusText)
}
this.data = await response.json();
this.myData = new Info(this.data);
} catch (error) {
this.error = error;
console.error("There was an error!", error);
}
}
}
}
</script>
You could stick the myData.getName() call inside a computed field that either returns the name if myData != null or an empty string (or whatever value you want when there is no data available).
Something like this
computed: {
getName() {
return this.myData ? this.myData.getName() : '';
}
}

Sending data with a file in a post request

I am using Angular 11 to send a file to my node / express.js back end how do I send data along with the file?
I have a schema called sources, and another called files the files schema contains the sources schema id in order to indicate which files belong to which sources.
In my angular app I loop over the data fetched from the source's documents to display them, each source displayed has an option to upload a file.
I want to be able to send the source id along with the file in my post request in order to store it on my database.
Here is the code I used :
source.component.ts
#Component({
selector: 'app-source',
templateUrl: './source.component.html',
styleUrls: ['./source.component.scss'],
})
export class SourceComponent implements OnInit {
showModal: boolean = false;
faUpload = faUpload;
#Input() datasource: {
_id: string;
name: string;
description: string;
imagePath: string;
};
#Input() searchPlaceHolder1: string;
#Input() searchPlaceHolder2: string;
isModalActive: boolean = false;
#Output() messageEvent = new EventEmitter<string>();
select: string = 'not selected yet';
searchText: string = '';
fileArr = [];
sheetArr = [];
fileObj = [];
form: FormGroup;
msg: string;
progress: number = 0;
isButtonVisible: boolean = true;
constructor(
public fb: FormBuilder,
private sanitizer: DomSanitizer,
public dragdropService: DragdropService
) {
this.form = this.fb.group({
txt: [null],
});
}
ngOnInit(): void {}
onSelect() {
this.select = 'selected';
}
sendMessage() {
this.messageEvent.emit(this.datasource.name);
}
upload(e) {
const fileListAsArray = Array.from(e);
fileListAsArray.forEach((item, i) => {
const file = e as HTMLInputElement;
const url = URL.createObjectURL(file[i]);
this.sheetArr.push(url);
this.fileArr.push({ item, url: url });
});
this.fileArr.forEach((item) => {
this.fileObj.push(item.item);
});
// Set files form control
this.form.patchValue({
txt: this.fileObj,
});
this.form.get('txt').updateValueAndValidity();
// Upload to server
this.dragdropService
.addFiles(this.form.value.txt)
.subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log('Request has been made!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header has been received!');
break;
case HttpEventType.UploadProgress:
this.progress = Math.round((event.loaded / event.total) * 100);
console.log(`Uploaded! ${this.progress}%`);
break;
case HttpEventType.Response:
console.log('File uploaded successfully!', event.body);
setTimeout(() => {
this.progress = 0;
this.fileArr = [];
this.fileObj = [];
this.msg = 'File uploaded successfully!';
}, 3000);
}
});
}
// Clean Url
sanitize(url: string) {
return this.sanitizer.bypassSecurityTrustUrl(url);
}
loading = { 1: false, 2: false, 3: false, 4: false };
doSomething(i: number) {
console.log('Clicked');
this.loading[i] = true;
setTimeout(() => {
this.loading[i] = false;
}, 2000);
}
selectItem() {
this.showModal = true;
}
}
drag-drop.service.ts
#Injectable({
providedIn: 'root',
})
export class DragdropService {
constructor(private http: HttpClient) {}
addFiles(sheets: File) {
var arr = [];
var formData = new FormData();
arr.push(sheets);
arr[0].forEach((item, i) => {
formData.append('txt', arr[0][i]);
});
return this.http
.post('http://localhost:4000/upload-file', formData, {
reportProgress: true,
observe: 'events',
})
.pipe(catchError(this.errorMgmt));
}
errorMgmt(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}
As for the back end code :
app.post("/upload-file", uploads.single("txt"), (req, res) => {
//convert csvfile to jsonArray
if (
req.file.mimetype === "application/vnd.ms-excel" ||
req.file.mimetype === "application/csv" ||
req.file.mimetype === "text / csv"
) {
const fileName = req.file.originalname;
csv({
delimiter: ";",
})
.fromFile(req.file.path)
.then((jsonObj) => {
//insertmany is used to save bulk data in database.
//saving the data in collection(table)
//finding the document using fileName and setting csvData as the jsonObj
sheetModel.findOneAndUpdate(
{ fileName: fileName },
{ $set: { csvData: jsonObj } },
{ upsert: true }, // if name does not exist insert
(err, csvData) => {
if (err) {
res.status(400).json({
message: "Something went wrong!",
});
} else {
res.status(200).json({
message: "File Uploaded Successfully!",
result: csvData,
});
}
}
);
});
}
Just add the additional fields to formData in the same way that you add the files:
formData.append('sourceId', sourceId);
It seems that you are using Multer middleware on the server. According to the documentation, "req.body will hold the text fields, if there were any".

Angular Node server returns You provided 'undefined' where a stream was expected

I am using angular 9 + universal. No errors while i run ng serve , then I build the app with npm run build:ssr and try to run with node : node dist/app/server/main.js and get the following error in terminal :
Node Express server listening on http://localhost:4000 TypeError: You
provided 'undefined' where a stream was expected. You can provide an
Observable, Promise, Array, or Iterable.
at subscribeTo (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:2547459)
at subscribeToResult (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:2775326)
at CatchSubscriber.error (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1997435)
at Observable_Observable._trySubscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952954)
at Observable_Observable.subscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952574)
at CatchOperator.call (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1996823)
at Observable_Observable.subscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952428)
at _task (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1751796)
at Observable_Observable.Observable.a.observer [as _subscribe] (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1752141)
at Observable_Observable._trySubscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952792)
As I've explored, my app does 2 api calls on start :
app.component.ts :
ngOnInit(){
get1();
get2();
}
get1() {
const loc = this.locationService.getPickupLocations().subscribe((data: Location[]) => {
this.pickupLocations = data;
this.formGroup.get(LocationFields.pickup).setValue(data[0].getId());
this.pickupLocationsList = this.pickupLocations.map((data): ISelectOption => {
return {
label: data.getName(),
value: data.getId(),
};
});
},
(error)=> {
console.log(error)
},
() => {
this.subs.add(loc);
this.pickupDateChange(this.formGroup.get(this.LocationFields.pickupDate).value);
});
}
get2() {
const drop = this.locationService.getDropOffLocations().subscribe((data: Location[]) => {
this.dropoffLocations = data;
this.formGroup.get(LocationFields.dropoff).setValue(data[1].getId());
this.dropoffLocationsList = this.dropoffLocations.map((data): ISelectOption => {
return {
label: data.getName(),
value: data.getId(),
};
});
},(error)=> {
console.log(error)
},
() => {
this.subs.add(drop);
});
}
LocationService.ts :
static locationsEndpoint = 'public/locations/rental';
getPickupLocations(): Observable<Location[]> {
const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', 'No-Auth': 'True' });
return this.http.get(`${LocationsService.locationsEndpoint}/pickup`, { headers: reqHeader }).pipe(
map((data: ILocationResponse) => this.hydrateCollectionData(data, LocationsHydrator))
);
}
getDropOffLocations(): Observable<Location[]> {
const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', 'No-Auth': 'True' });
return this.http.get(`${LocationsService.locationsEndpoint}/dropoff`, { headers: reqHeader }).pipe(
map((data: ILocationResponse) => this.hydrateCollectionData(data, LocationsHydrator))
);
}
And Interceptors :
private static BASE_URL = environment.apiUrl;
readonly HEADER_AUTHORIZATION = 'Authorization';
readonly HEADER_ACCEPT = 'Accept';
readonly HEADER_CONTENT_TYPE = 'Content-Type';
readonly ACCEPT_LANGUAGE = 'Accept-Language';
constructor(
private authService: AuthService,
private localeService: LocaleService
) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('skip')) {
return next.handle(req);
}
if (req.url.startsWith('./assets')) {
return next.handle(req);
}
req = req.clone({
url: this._prefixUrl(req.url)
});
req = req.clone({
headers: req.headers.set(this.HEADER_ACCEPT, 'application/json')
});
req = req.clone({
headers: req.headers.set(this.HEADER_CONTENT_TYPE, 'application/json')
});
req = req.clone({
headers: req.headers.set(this.ACCEPT_LANGUAGE, this.localeService.getLocale())
});
// Set token if exists
const token = this.authService.getToken();
if (token) {
req = req.clone({
headers: req.headers.set(this.HEADER_AUTHORIZATION, `Bearer ${token}`)
});
}
return next.handle(req).pipe(
catchError((httpErrorResponse: HttpErrorResponse) => {
if(httpErrorResponse.error !== undefined){
const customError: ApiErrors = {
name: httpErrorResponse.error.name,
message: httpErrorResponse.error.message,
errors: httpErrorResponse.error.errors
};
return throwError(customError);
}
})
);
}
private _prefixUrl(path: string): string {
if (path.indexOf('/') === 0) {
path = path.substr(1, path.length - 1);
}
return `${Interceptor.BASE_URL}/${path}`;
}
I tried without these calls , tried to comment one of them.
When i disable those calls (comment them) , app works fine.
When i call them later, after i commented them, (onclick), app works fine.
When i disable interceptors , it works (SO the problem is in them, what to change ?? )
How to fix it ? And why it is happening ?
The trace seems to suggest the problem is related to CatchOperator.
I see only one place where catchError operator is used in your code
return next.handle(req).pipe(
catchError((httpErrorResponse: HttpErrorResponse) => {
if(httpErrorResponse.error !== undefined){
const customError: ApiErrors = {
name: httpErrorResponse.error.name,
message: httpErrorResponse.error.message,
errors: httpErrorResponse.error.errors
};
return throwError(customError);
}
})
);
In your code you seem to assume that the function passed to catchError will receive always an instance of HttpErrorResponse which is not the case. catchError will be used for any error and so the function passed to it can receive any type of error.
What happens if httpErrorResponse.error is null but you still have an error in the upstream Observables somewhere? According to the code above you do not return anything, so this may be the reason why the log says You provided 'undefined' where a stream was expected.
Rather than not doing anything, you can throw an error, so that you should get the details of the error causing the fact that you enter catchError operator, so something like this
catchError(err => {
if(error instanceof HttpErrorResponse && httpErrorResponse.error !== undefined){
const customError: ApiErrors = {
name: httpErrorResponse.error.name,
message: httpErrorResponse.error.message,
errors: httpErrorResponse.error.errors
};
return throwError(customError);
} else {
throw err
}
})
);

Nested Observables behaving differently in Ionic2/Angular2 App

I am creating an ionic login module, where in there are 2 observables , 1 inside another, Not sure if this is the correct way of implementation,
Here I am trying to call getHTTP() method, get a string, if the string is not empty then set it in ionic-storage varaible and then verify before logging in
Since Observables are async - getHTTP() is getting completed after the flow of login(credentials) , Help me out
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Observable';
import {Headers} from '#angular/http';
import { Response } from '#angular/http';
import { Storage } from '#ionic/storage';
export class User {
name: string;
password: string;
url: string;
constructor(name: string, password: string, url: string) {
this.name = name;
this.password = password;
this.url = url;
}
}
/*
Generated class for the AuthService provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class AuthService {
currentUser: User;
data = '';
constructor(public http: Http,private storage: Storage) {
console.log('Hello AuthService Provider');
}
// Make a call to Get CSRF and check if we have access
public getHTTP(credentials) {
let responseCSRF ;
const headers: Headers = new Headers();
headers.append('Authorization', 'Basic ' + btoa(credentials.user + ':' + credentials.password));
headers.append('Content-Type', 'application/json');
console.log(headers);
console.log('Clearing cache');
this.storage.set('CSRF', '');
this.storage.set('Auth',btoa(credentials.user + ':' + credentials.password));
this.storage.set('url', credentials.url);
//return
responseCSRF = this.http.get('http://' + credentials.url +'/Windchill/servlet/rest/security/csrf', {
headers: headers
}).map((response: Response) => response.json());
//console.log(typeof(responseCSRF))
responseCSRF.subscribe(x => {
console.log('CSRF ->' + x.items[0].attributes.nonce)
this.data = x.items[0].attributes.nonce;
if(typeof this.data!='undefined' && this.data) {
this.storage.set('CSRF', this.data);
}
});
return responseCSRF;
}
public login(credentials) {
if (credentials.user === null || credentials.password === null || credentials.url === null ) {
return Observable.throw("Please insert credentials ");
} else {
return Observable.create(observer => {
// At this point make a request to your backend to make a real check!
let access = false;
this.getHTTP(credentials).subscribe (
(resBody) => console.log('Boby is '+resBody),
error => console.error('Error from auth-service: ' + error))
, () => console.log('Completed!' + 'Auth' );
this.storage.get('CSRF').then((val) => {
console.log('Your CSRF is'+ val);
if(val!='undefined') {
access = true;
}
});
observer.next(access);
observer.complete();
});
}
}
public getUserInfo() : User {
return this.currentUser;
}
public logout() {
return Observable.create(observer => {
this.currentUser = null;
observer.next(true);
observer.complete();
});
}
}
In the Console
Headers {_headers: Map(2), _normalizedNames: Map(2)}
auth-service.ts:49 Clearing cache
auth-service.ts:57 pluck -->[object Object]
auth-service.ts:83 Your CSRF is
auth-service.ts:59 CSRF ->RkPYp+UtGGMRB+8NJHCr9rJ6WhBHdIVCfim585xXKgZ1TKUmf3v39tBqVRkjSb93dgWi4oF3KF4rNts0c3frktUdIFokNNVrMSGM47V3KwQhP8A5ARKr5rBsaxtmOtI=
auth-service.ts:78 Boby is [object Object]
Try to put your storage.get logic inside subscription handler:
return Observable.create(observer => {
// At this point make a request to your backend to make a real check!
let access = false;
this.getHTTP(credentials).subscribe(
(resBody) => {
console.log('Boby is ' + resBody);
this.storage.get('CSRF').then((val) => {
console.log('Your CSRF is' + val);
if (val != 'undefined') {
access = true;
}
observer.next(access);
observer.complete();
});
},
error => console.error('Error from auth-service: ' + error),
() => console.log('Completed!' + 'Auth'));
});

Categories

Resources