Angular 7 Cannot read property 'map' of undefined - angular-material

I'm having this problem to use mat-autocomplete async, I have tried several solutions mainly from here and even then I did not succeed. follow my code ... Thanks
component.ts
filteredEmpresas: Observable<IEmpresaResponse>;
empresasForm: FormGroup;
this.empresasForm = this.fb.group({
empresaInput: null
})
this.filteredEmpresas = this.empresasForm.get('empresaInput').valueChanges
.pipe(
debounceTime(300),
switchMap(value => this.appService.search({text: value}, 1))
);
service.ts
search(filter: {text: string} = {text: ''}, page = 1):
Observable<IEmpresaResponse> {
return this.http.get<IEmpresaResponse>(this.apiURL + '/busca/'+filter.text)
.pipe(
tap((response: IEmpresaResponse) => {
response.results = response.results
.map(empresa => new Empresa(empresa.idEmpresa, empresa.nomeEmpresa, empresa.ativo));
return response;
})
);
}
class.ts
export class Empresa {
constructor(public idEmpresa: number, public nomeEmpresa: string, public ativo: Boolean) {}
}
export interface IEmpresaResponse {
total: number;
results: Empresa[];
}

Related

Ng2-Charts Unexpected change chart's color when data is changed

In my project I use ng2-charts. All works fine and chart is shown as expected (data, labels, chart's colors), but when data is changed then color of chart become grey by default. May someone help to correct problem with chart's color?
Here is my code:
import { ChartDataSets } from 'chart.js';
import { Color, Label } from 'ng2-charts';
...
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[];
lineChartLabels: Label[];
lineChartLegend = true;
lineChartType = 'line';
lineChartColors: Color[] = [
{
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)'
},
{
backgroundColor: 'rgba(77,83,96,0.2)',
borderColor: 'rgba(77,83,96,1)'
}];
options: any = {
legend: { position: 'bottom' }
}
constructor(
...//inject services
) {
super();
this.initData();
};
initData(): void {
this.lineChartData = [];
this.lineChartLabels = [];
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
this.isLoading = true;
var limitPromise = this.juridicalLimitService.getPrimary(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
var analyticsPromise = this.juridicalAnalyticsService.getUsedEnergy(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels = data[1].map(e => e.Period);
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Bid'
},
{
data: data[1].map(e => e.Used),
label: 'Used'
}
);
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
export abstract class BidComponent {
cabinetId: number;
isLoading: boolean = false;
#Input("periods") periods: BaseDictionary[];
#Input("cabinetId") set CabinetId(cabinetId: number) {
this.cabinetId = cabinetId;
this.initData();
}
abstract initData(): void;
}
As you can see this component is partial and I use setter to listen of cabinetId changes.
Here is html part:
...
<canvas baseChart width="400" height="150"
[options]="options"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[legend]="lineChartLegend"
[chartType]="lineChartType"
[colors]="lineChartColors"></canvas>
...
And I use this component as:
<app-juridical-bid-primary [cabinetId]="cabinetId"></app-juridical-bid-primary>
I find similar question similar question, but, unfortunately, don't understand answer
After some hours of code testing I find answer. It is needed to correct code from question:
...
import * as _ from 'lodash'; //-- useful library
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[] = [];
lineChartLabels: Label[] = [];
...
initData(): void {
/*this.lineChartData = [];
this.lineChartLabels = [];*/ //-- this lines is needed to remove
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
...
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels.length = 0;
this.lineChartLabels.push(...data[1].map(e => e.Period));
if (_.isEmpty(this.lineChartData)) {
//-- If data array is empty, then we add data series
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Замовлені величини'
},
{
data: data[1].map(e => e.Used),
label: 'Використано'
}
);
} else {
//-- If we have already added data series then we only change data in data series
this.lineChartData[0].data = data[1].map(e => e.Limit);
this.lineChartData[1].data = data[1].map(e => e.Used);
}
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
As I understand ng2-charts, if we clean dataset (lineChartData) and add new data then the library understand this as create new series and don't use primary settings for the ones. So we have to use previous created series.
I hope it will be useful for anyone who will have such problem as I have.

Typeorm BaseEntity create function: how to deeply create an entity?

I have a Patient entity:
#Entity()
#ObjectType()
#InputType('PatientInput')
export class Patient extends BaseEntity {
#PrimaryGeneratedColumn()
#Field(type => Int, { nullable: true })
id: number
#Column({ length: 500 })
#Field({ nullable: true })
firstName?: string
#Column({ length: 500 })
#Field({ nullable: true })
lastName?: string
#Field(type => MedicalCondition, { nullable: true })
#OneToMany(type => MedicalCondition, medicalCondition => medicalCondition.patient, { cascade: true })
medicalConditions?: MedicalCondition[]
}
And a MedicalCondition entity:
#ObjectType()
#Entity()
#InputType('medicalCondition')
export class MedicalCondition extends BaseEntity {
#Field({nullable: true})
#PrimaryGeneratedColumn()
id: number
#Field(type => Int, { nullable: true })
#RelationId((medicalCondition: MedicalCondition) => medicalCondition.patient)
#Column()
patientId?: number
#Field(type => Patient, { nullable: true })
#ManyToOne(type => Patient, patient => patient.medicalConditions, {
onDelete: 'CASCADE',
})
#JoinColumn()
patient?: Patient
#Field()
#Column()
name: string
#Field({nullable: true})
#Column()
startDate?: Date
}
When trying to create an instance of patient using the BaseEntity create function, the patient is created but in the medical conditions array only containes the last element and all the rest disappear even if there were many elements.
#Mutation(returns => Patient)
async create(#Args('patient') newPatientInput: Patient, #CurrentUser() user: User, #Useragent() useragent): Promise<Patient> {
const newPatient: Patient = Patient.create(newPatientInput)
const event = createEvent({ useragent, actionType: 'add_patient', userId: user.id })
const p = await this.patientService.create(newPatient, event)
return p
}
create = async (patient: Patient, event: EventLog): Promise<Patient> => {
const isExist = await this.isPatientExist({ firstName: patient.firstName, lastName: patient.lastName, birthDate: patient.birthDate })
if (isExist > 0) {
throw new Error('Patient already exist')
} else {
const transactionResult = runTransaction(async (em) => {
const eventLog = EventLog.create(event)
await em.save(eventLog)
return await em.save(patient)
})
return transactionResult
}
}
I tried to directly save the entity without invoting create():
return await em.save(Patient, patient)
and that was the result of the saving:
medicalConditions:
[ { id: 36,
name: 'heartDisease',
startDate: 2016-07-31T21:00:00.000Z,
kin: 'father',
note: '',
patientId: 26,
createdAt: 2019-11-24T17:11:22.376Z,
updatedAt: 2019-11-24T17:11:22.376Z },
{ id: null,
name: 'previousHeartAttack',
startDate: 2018-04-30T21:00:00.000Z,
kin: 'brother',
note: '' },
{ id: null,
name: 'highBloodPressure',
startDate: 2018-03-31T21:00:00.000Z,
kin: 'sister',
note: '' } ],
Tried google for it and didn't find any known issue.
So the willing result would be to create an entity deeply, is that a possible behavior?

Ngrx-effect doesn't sending payload in action on iOS

For some time I have been trying to find a solution to my problem, however, nothing has worked so far. I'm working on Ionic 4 application with Angular 8 and Ngrx. I created #Effect that calling a service which calling http service and then I need to dispatch two actions. One of them have a payload also.
Everything working fine in development (browsers). I've tried on Chrome, Firefox, Safari. Problem is appearing when I'm trying on the iPhone. On the iPhone payload sending to action is empty object {} instead of object with proper fields.
I've tried to build in non-production mode, disabling aot, build-optimizer, optimization.
Store init:
StoreModule.forFeature('rental', reducer),
EffectsModule.forFeature([RentalServiceEffect]),
Store:
export interface Contract {
address: string;
identity: string;
endRentSignature?: string;
}
export interface RentalStoreState {
status: RentStatus;
contract?: Contract;
metadata?: RentalMetadata;
summary?: RentalSummary;
carState?: CarState;
}
export const initialState: RentalStoreState = {
status: RentStatus.NOT_STARTED,
contract: {
address: null,
identity: null,
endRentSignature: null,
},
};
Action:
export const rentVerified = createAction(
'[RENTAL] RENT_VERIFIED',
(payload: Contract) => ({ payload })
);
Reducer:
const rentalReducer = createReducer(
initialState,
on(RentActions.rentVerified, (state, { payload }) => ({
...state,
contract: payload,
status: RentStatus.RENT_VERIFIED
})));
export function reducer(state: RentalStoreState | undefined, action: Action) {
return rentalReducer(state, action);
}
Method from a service:
public startRentalProcedure(
vehicle: Vehicle,
loading: any
): Observable<IRentalStartResponse> {
loading.present();
return new Observable(observe => {
const id = '';
const key = this.walletService.getActiveAccountId();
this.fleetNodeSrv
.startRent(id, key, vehicle.id)
.subscribe(
res => {
loading.dismiss();
observe.next(res);
observe.complete();
},
err => {
loading.dismiss();
observe.error(err);
observe.complete();
}
);
});
}
Problematic effect:
#Effect()
public startRentalProcedure$ = this.actions$.pipe(
ofType(RentalActions.startRentVerifying),
switchMap(action => {
return this.rentalSrv
.startRentalProcedure(action.vehicle, action.loading)
.pipe(
mergeMap(response => {
return [
RentalActions.rentVerified({
address: response.address,
identity: response.identity
}),
MainActions.rentalProcedureStarted()
];
}),
catchError(err => {
this.showConfirmationError(err);
return of({ type: '[RENTAL] START_RENTAL_FAILED' });
})
);
})
);

how to optimisticResponse a connection property in relay modern mutation?

i have a comments connection in a mutation, this is the query:
export default mutationFromQuery(graphql`
mutation AddBookMutation($input: AddBookInput! $count: Int $cursor: String ) {
addBook(input: $input) {
book {
__typename
cursor
node {
id
title
owner
createdAt
comments(first: $count, after: $cursor)
#connection(key: "BookComments_comments", filters: []) {
__typename
edges {
node {
id
}
}
}
}
}
}
}
`)
This is how i did my optimisticUpdater that don't work:
optimisticUpdater: (store) => {
const userProxy = store.get(viewerId)
const owner = userProxy.getValue('username')
const id = uuidv1();
const book = store.create(id, 'Book');
book.setValue(id, 'id');
book.setValue(bookTitle, 'title');
book.setValue(owner, 'owner');
book.setValue(Date.now(), 'createdAt');
const comments = store.create(uuidv1(), 'comments')
comments.setLinkedRecords([], 'edges')
const pageInfo = store.create(uuidv1(), 'pageInfo')
pageInfo.setValue(null, 'endCursor')
pageInfo.setValue(false, 'hasNextPage')
pageInfo.setValue(false, 'hasPreviousPage')
pageInfo.setValue(null, 'startCursor')
comments.setLinkedRecord(pageInfo, 'pageInfo')
book.setLinkedRecord(comments, 'comments')
const bookEdge = store.create(uuidv1(), 'BookEdge');
bookEdge.setLinkedRecord(book, 'node');
console.log('bookEdge ', bookEdge)
booksUpdater(userProxy, bookEdge);
},
The problem i have is that comments always ends up on undefined as you can see above i've already set it. I also did this but i am still not getting an optimistic UI:
optimisticResponse: {
addBook: {
book: {
__typename: 'BookEdge',
cursor: uuidv1(),
node: {
id: uuidv1(),
title: bookTitle,
owner: username,
createdAt: Date.now(),
comments: {
__typename: 'CommentConnection',
edges: [],
pageInfo: {
endCursor: null,
hasNextPage: false
}
}
}
}
}
},
App don't crash with optimisticResponse code but no optimistic UI effect, but with the optimisticUpdater it's crashing with comments being undefined, for now I am settling with my updater:
updater: (store) => {
const userProxy = store.get(viewerId)
const payload = store.getRootField('addBook');
booksUpdater(userProxy, payload.getLinkedRecord('book'));
},
since the comments is undefined I guess we cannot use this for optimistic effect:
const comments = store.create(uuidv1(), 'comments')
comments.setLinkedRecords([], 'edges')
book.setLinkedRecord(comments, 'comments')
on my Book, this is the query which has the comments fragment that is undefined on optimistic update with the code above:
export default createRefetchContainer(
BookItem,
{
book: graphql`
fragment BookItem_book on Book
#argumentDefinitions(
count: { type: "Int", defaultValue: 5 }
cursor: { type: "String", defaultValue: null }
) {
id
title
owner
createdAt
...BookComments_book
}
`
},
graphql`
query BookItemQuery($id: ID!, $count: Int, $cursor: String) {
book: node(id: $id) {
...BookItem_book #arguments(count: $count, cursor: $cursor)
}
}
`
);
and now the query for the comments component where it gets the book.comments.edges is undefined:
export default createPaginationContainer(
BookComments,
{
book: graphql`
fragment BookComments_book on Book
#argumentDefinitions(
count: { type: "Int", defaultValue: 3 }
cursor: { type: "String", defaultValue: null }
) {
id
title
comments(first: $count, after: $cursor)
#connection(key: "BookComments_comments", filters: []) {
__typename
edges {
node {
id
text
owner
createdAt
}
}
pageInfo {
startCursor
endCursor
hasPreviousPage
hasNextPage
}
}
}
`
},
{
direction: 'forward',
getConnectionFromProps: (props) => props.book && props.book.comments,
getFragmentVariables: (prevVars, totalCount) => ({
...prevVars,
count: totalCount
}),
getVariables: (props, { count, cursor }, _fragmentVariables) => ({
count,
cursor,
id: props.book.id
}),
query: graphql`
query BookCommentsQuery($id: ID!, $count: Int, $cursor: String) {
book: node(id: $id) {
...BookComments_book #arguments(count: $count, cursor: $cursor)
}
}
`
}
);
maybe this is an anti pattern? but i just wanted to have a optimistic effect for this
Some things are still not very clear to me about how those components and queries work, so I'll update this answer later. (I don't know if you want to return new book optimistically from node() query or add it to some list/connection of books)
Please check if I used correct type names (CommentConnection / CommentsConnection, etc)
optimisticUpdater: (store) => {
const userProxy = store.get(viewerId)
const owner = userProxy.getValue('username')
const commentsParams = { // must be same keys and values as in comments(first: $count, after: $cursor)
first: count,
after: cursor
}
// Create Book
const id = uuidv1();
const book = store.create(id, 'Book');
book.setValue(id, 'id');
book.setValue(bookTitle, 'title');
book.setValue(owner, 'owner');
book.setValue(Date.now(), 'createdAt');
// Create comments connection
const comments = store.create(uuidv1(), 'CommentConnection')
comments.setLinkedRecords([], 'edges')
// Create PageInfo
const pageInfo = store.create(uuidv1(), 'PageInfo')
pageInfo.setValue(null, 'endCursor')
pageInfo.setValue(false, 'hasNextPage')
pageInfo.setValue(false, 'hasPreviousPage')
pageInfo.setValue(null, 'startCursor')
// Link created records
comments.setLinkedRecord(pageInfo, 'pageInfo')
book.setLinkedRecord(comments, 'comments', commentsParams) // don't forget commentsParams with same values as are used in comments graphql query
// I'm not sure about this final part, because I don't really get how that app works, but if you want this book to show as optimistic response for `node(id: $id)`, you'll do something like this:
store.getRoot().setLinkedRecord(book, 'node', { id: id }) // same id as used in BookItemQuery
}

Angular 2 Reactive Forms Async Custom Validation throws "subscribe not a function"

I've already tried every permutation of the answers to [angular2 async validation this.subscribe exception? but i'm still getting the exception.
import {AsyncValidatorFn, AbstractControl } from '#angular/forms';
export function userNameShouldBeUnique(): AsyncValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
return new Promise(resolve => {
setTimeout(() => {
if (control.value == 'mosh')
resolve({ shouldBeUnique: true });
else
resolve(null);
}, 1000);
});
}
}
and in the component (the last attempt):
this.myForm = this.fb.group({
username: [
'',
Validators.compose([Validators.required, forbiddenNameValidator(/bob/)]),
Validators.composeAsync([userNameShouldBeUnique])
],
password: ['', Validators.required]
});
so what am I doing wrong? Thanks
The solution is:
import { AsyncValidatorFn, AbstractControl } from '#angular/forms';
export function UniqueValidator(): AsyncValidatorFn {
return (control: AbstractControl): Promise<any> => {
return new Promise<any>(resolve => {
setTimeout(() => {
if (control.value === 'mosh')
resolve({ shouldBeUnique: true });
else
resolve(null);
}, 1000);
});
};
};
Now you have return types well configured. To be added as custom validation:
username: ['', Validators.required, UniqueValidator()]
Just tested and it works ;)

Resources