Assign #Input() object properties to the component - javascript

I am having an Angular component with a single #Input element:
#Input('member') member: Member;
the Member interface looks like this:
export interface Member {
name: string;
surname: string;
display_name: string;
positions: number[];
image_url: string;
email: string;
address: string;
office_phone: string;
phone: string;
whatsapp: string;
skype: string;
}
I want to access the member fields from html template like {{ name }}, {{ email }} etc...
wtihout prefixing each of them.
For example, I do not want to access these properties like {{ member.name }}, {{ member.email }}, I like the shortcut version.
The component will have only a single #Inpurt property - the Member object.
Is there a nice way to reassign the member. properties to the Angular component? Or any other way to use a shortcut, I mensioned above?

Not a good practice, but you should spread that object into your component :
ngOnInit() {
Object.entries(this.member).forEach(([key, value]) => this[key] = value);
// OR
Object.assign(this, this.member);
}
You can now use them as if they were class properties. Be warned that it will override any other property with the same name on your class, so you should be careful about that solution and instead use member.XXX.

Related

How to use Typescript and Interfaces when passing props down through React components?

I'm passing down a pretty large array of objects through several React components. I was wondering, what is the syntax for writing the types of all the properties in each object (the objects are nested several times)?
I currently have interfaces like below. These are two components, MainContent, which passes props down into Chart:
MainContent component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function MainContent({ comparatorData, queryData }: params) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}
and Chart component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
// comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function Chart({ queryData }: params): JSX.Element {
...
You can see how redundant it is to be naming these giant, several-times-nested interfaces before every component that uses this array of objects. Is this normal for Typescript? Is there a better way to do something like this? Or does all this data need to be typed upon being passed down through every component?
What forces you to define these identical interfaces explictly for each component?
On the contrary, factorizing them would be the normal choice: that way, they are defined in a single place (single source of truth), and by importing them, you explictly say that you re-use the exact same types.
// Chart.tsx
export interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
export interface DataTypes {
jobId: string;
jobTitle: string;
// etc.
}
export default function Chart({
queryData
}: {
queryData: QueryTypes[];
}) {}
// Main.tsx
import Chart, { QueryTypes } from ".Chart";
import Summary, { ComparatorTypes } from "./Summary"; // Same for ComparatorTypes
export default function MainContent({
comparatorData,
queryData
}: {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}

Get interface that can have multiple query responses in GraphQL

Currently in my GraphQL query, i can receive a single array response. But my response can be multiple arrays and there can be null in between of the response too depending on the input. Currenly if i have two inputs and suppose i'm getting an array for the first input and error for the second, it will throw an error. I need to fix that by showing null if error and returning the array if success. Here's what i have right now:
export const LookupUsers = gql`
query {
lookupUsers(ethAddresses: ["0x433232bC8C604d0308B71Defdgd1Da86"]) {
...User
}
}
fragment Identifiable on Node {
__typename
id
}
fragment User on User {
...Identifiable
ethAddress
firstName
lastName
}
`;
export interface Result {
readonly user: null | Array<null | User> ;
readonly lookupUsers: Array<null | LookupUsersType>;
}
export interface LookupPayload {
readonly payload: Array<string>;
}
export interface User extends Identifiable {
readonly ethAddress: string;
readonly emailAddress?: string;
readonly firstName?: string;
readonly lastName?: string;
}
export interface LookupUsersType extends Identifiable {
readonly ethAddress: string;
readonly emailAddress: string;
readonly firstName?: string;
readonly lastName?: string;
}
export interface Identifiable {
readonly __typename: string;
readonly id: string;
}
Where am i going wrong with this ?

What is difference between def interface and dto inerface in Angular?

I am working on the project, which was started by someone else. There are two interface files in the model folder def and dto. The difference between def and dto interface files is not clear to me. Could any expereince developer let me know what is the difference and when and how to use dto instead of def and viceversa. Thanks in advance.
vendor-def.interface.ts:
import { SourceType, VendorType } from '../shared/enums/vendor-type.enum';
export interface VendorDef {
vendorId: string;
companyCode: string;
name: string;
acronym: string;
alias: string;
legalId: string;
vendorType: VendorType;
sourceType: SourceType;
fiscalCode: string;
}
export interface VendorFormDef {
sourceType: SourceType;
companyCode?: string;
previousMainCompany?: string;
}
export interface InUsageDef {
acronym: boolean;
legalId: boolean;
fiscalCode: boolean;
}
vendor-dto.interface.ts
import { SourceType, VendorType } from '../shared/enums/vendor-type.enum';
export interface VendorDto {
data: VendorDataDto[] | VendorDataDto;
errors?: VendorErrorsDto;
}
export interface VendorDataDto {
attributes: VendorAttributesDto;
id: string;
}
export interface VendorErrorsDto {
code: string;
title: string;
detail: string;
}
export interface VendorCreateDto {
companyCode: string;
name: string;
acronym: string;
legalId: string;
fiscalCode: string;
vendorType: VendorType;
sourceType: SourceType;
}
Basically, it's used to separate what your API gives you from the objects you will manipulate.
VendorDTO is your API response (hence the presence of the data and errors fields)
VendorDef is the definition of the object you will manipulate in your app.
It is common to have a transformer from VendorDTO to VendorDef for when you request the data and a transformer from VendorDef to VendorDTO for when you want to push an addition/update on your API.
It is not restricted to Typescript or Angular, so you might want to check your question's tags.

TSX: Props type definition for a number input

In JSX/TSX syntax from react, all inputs seems to be define with the same props declaration which is InputHTMLAttributes:
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
accept?: string;
alt?: string;
autoComplete?: string;
autoFocus?: boolean;
capture?: boolean | string; // https://www.w3.org/TR/html-media-capture/#the-capture-attribute
checked?: boolean;
crossOrigin?: string;
disabled?: boolean;
form?: string;
formAction?: string;
formEncType?: string;
formMethod?: string;
formNoValidate?: boolean;
formTarget?: string;
height?: number | string;
list?: string;
max?: number | string;
maxLength?: number;
min?: number | string;
minLength?: number;
multiple?: boolean;
name?: string;
pattern?: string;
placeholder?: string;
readOnly?: boolean;
required?: boolean;
size?: number;
src?: string;
step?: number | string;
type?: string;
value?: string | ReadonlyArray<string> | number;
width?: number | string;
onChange?: ChangeEventHandler<T>;
}
I want to define a specific type for only an input of type number so that TypeScript complains if I use a checked or disabled prop on it:
<!-- invalid `checked` prop -->
<input type="number" step="any" min="0" max="100" value="22.33" checked={true} />
So is there already a type definition for that coming from React?
I know that I can just define it like the following but I would prefer to use an official source:
interface InputNumberProps {
type: 'number';
max?: number | string;
min?: number | string;
...
}
I think the simplest solution would be to wrap input into your own component and provide custom type definitions. As far as I know, there is nothing like that coming from official React types. This is done because HTML itself is not strongly typed and it is valid to do something like <input type="number" checked />
As for type definition something like that would suffice:
interface NumberInputProps {
type: "number"
value: number
min?: number
max?: number
}
interface TextInputProps {
type?: "text"
value: string
}
interface CheckboxInputProps {
type: "checkbox"
checked?: boolean
}
type InputProps = NumberInputProps | TextInputProps | CheckboxInputProps
const Input: React.FC<InputProps> = (props) => <input {...props} />
That is a lot of typings for sure (and this does not event include things like events, class names etc). Also, this requires even more JS code if you want to provide stronger types for something like number fields (converting strings into numbers etc.)
If you want to modify the original InputHTMLAttributes (which is a better solution than writing your own types from scratch) interface, overriding and/or omitting some of the fields there are helper types like Omit that would allow you to do so.
type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "checked" | "min" | "max"> & (NumberInputProps | TextInputProps | CheckboxInputProps)
You can also split different input types into different components if you find writing too much logic in one component. Do whatever suits you
P.S. on the second note. This sounds like type over-engeneering
You can use and extends the official types:
export type InputProps = React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>;
interface NumberInputProps extends Omit<InputProps, 'checked' | 'disabled'> {
value: number
}
Using this code you exclude checked and disabled properties and overrides value to allow only numbers instead string | ReadonlyArray | number

Problems writing to a nested array of objects in Angular

I have a model component that defines the data structure that I'm using. I then use that model in another component and try to write data to it. I'm having challenges writing to the 'nested' object array in my data structure. I'm pretty sure it's because I haven't declared the nested array correctly (instantiated it) but can't quite figure out how to do it properly. No question it's a limitation in my knowledge but after thrashing on it for several hours I figured I'd reach out for help. Any guidance would be appreciated.
Simplified stackblitz below...
https://stackblitz.com/edit/angular-bgk5fo
export class Adventure {
id ? : string;
companylogo: string;
guideavatar: string;
quizavatar: string;
active: boolean;
treasureimage: string;
poi: [{
completedflag: boolean;
firsthint: string;
secondhint: string;
thirdhint: string;
quizanswer: string;
quizfail: string;
quizoptionone: string;
quizoptiontwo: string;
quizoptionthree: string;
quizquestion: string;
quizsuccess: string;
sortorder: number;
target: string;
}];
}
import {
Component
} from '#angular/core';
import {
DataModel
} from './data.model'
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular';
testModel = new DataModel();
createArray() {
this.testModel.firstname = 'Jack';
this.testModel.lastname = 'Brown';
this.testModel.pet[0].petalive = true;
this.testModel.pet[0].petname = 'Whiskers';
this.testModel.pet[0].pettype = 'Cat'
}
}
there are some fundamental issues with your code.
I believe you want to make use of DataModel as for structural typing. Therefore, if you wanna define a class/interface for type checking, your data.model.ts should be defined this way:
export interface DataModel {
firstname: string;
lastname: string;
pet: Array<Pet>;
}
interface Pet {
petname: string;
pettype: string;
petalive: boolean;
}
On your component.ts, you will need to initialise and define your testModel (of type DataModel). On your createArray() method, you can simply assign the properties this way:
testModel: DataModel = {
firstname: undefined,
lastname: undefined,
pet: []
};
createArray(){
this.testModel.firstname = 'Jack';
this.testModel.lastname = 'Brown';
this.testModel.pet.push({
petalive: true,
petname: 'Whiskers',
pettype: 'Cat'
});
console.log(this.testModel);
}
This is a solution with array is type any
https://stackblitz.com/edit/angular-shotgr?file=src/app/app.component.ts
Hope that it's help for you
Result

Categories

Resources