I often have needs to extend my API model with parameters I use just in component view.
For Example I have a model:
export class Person {
name: string;
surname: string;
address: string;
}
It is something I get from API:
getPersons(): Observable<Person[]> {
return this.httpClient.get<Person[]>`${environment.API}/person`);
}
When I get this in my component I often need to extend model with parameter/attribute I get in data processing after request or just simple 'active'/'select' parameter for UI visualization tracking.
Which approach to use for this purpose. I know for 2 solutions:
1) Add parameter to the class model even if that parameter do not participate in server response, just separate them from standard parameters:
export class Person {
name: string;
surname: string;
address: string;
ui_active: boolean; // I know that those parameters are not from API
ui_fullName: string; // response but they are here to make my life easier
}
2) Make another extend class with those parameters:
export class PersonExtended extends Person {
ui_active: boolean;
ui_fullName: string
}
But this approach complicate thing since I have 2 models, and I need to switch between to them all the time.
What is the best practice for this kind of situation ?
Just make those fields optional with the ?-operator:
export class Person {
name: string;
surname: string;
address: string;
ui_active?: boolean;
ui_fullName?: string;
}
Thus, you can use them but you don't have to.
EDIT:
If you have to remove them somewhen use the following generic method:
private omitFields<T>(object: T, fields: Array<string>): T {
for (let field of fields) {
delete object[field];
}
return object;
}
And use it, for example, like this:
this.omitFields(el, ['ui_active', 'ui_fullName']);
I think codewise second option is right, but as far as I know there is nothing wrong with the first option, just make sure the new parameters can be null or ignored some way so you dont get errors if you dont set them where they are not needed.
Related
Im sure this is a really common issue but I just cannot seem to find any accepted solutions.
When combining typeorm and typegraphql, you create entities with the properties of the entity. However typeorm also allows for hooks such as beforeInsert to be added to the entity.
The issue im having is that the entity includes these hooks as properties which are not returned from the database e.g.
// Define the entity
#Entity()
#ObjectType()
export class MyEntity extends CustomBaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn("uuid")
id: string;
#Field()
#Column({ type: "bigint", nullable: true })
created: number;
#BeforeInsert()
beforeUpdate() {
this.created = Date.now();
}
}
// Pull the entity from the database
const myEntityRow = myEntityRepository.findOneWhere({id})
// As you can see there is a type mismatch
// The type is MyEntity type (including the beforeInsert method) even though there is no
// beforeInsert prop on the actual entity
console.log(
myEntityRow // { id: 1, created: 123 }
)
Meaning that something like this does not work:
const destructuredEntity = {...myEntityRow}
await myEntityRepository.save(destructuredEntity) // Typeerror - missing properties "beforeInsert"
Right now i'm probably just thinking that I need to remove these hook functions and just put any methods like this within the service, any ideas?
and just put any methods like this within the service, any ideas?
that's definitely the best choice.
It's better not to use inversion of control in case you can avoid it or you don't have some significant advantages because of using it.
Just put created initialization in constructor or make it default in your database.
Let's assume we have an Interface Customer:
export interface Customer {
name: string;
age: number;
phone: string;
}
Now we have a parent component and we want to pass an instance of Customer to the child component:
<child-component [customer]="instanceOfCustomer"></child-component>
After that we need to use #Input decorator to get the data in the child component. (We assume passing the instance is mandatory to use this component and this component will not work without passing the instance)
#Input() customer:Customer;
The problem is that we need to initialize customer somewhere and I think there are 3 approaches to avoid the error in Typescript:
Initializing an empty customer in the constructor:
this.customer = {
name: "";
age: 0;
phone: "";
}
Change strictPropertyInitialization to false
Just adding the ! prefix (Which I think I should use because no one should use this child component without passing the relative customer)
#Input() customer!:Customer;
I want to know what approach should I use to avoid property initialization error in case of using #Input decorator.
you should use setter https://angular.io/guide/component-interaction#intercept-input-property-changes-with-a-setter
or you can also use your 3rd approach.
there is already some answers for this too.
refer below answers for more details :
Make directive #Input required
To avoid TS from complaining I'll suggest initializing it like this:
#Input() customer: Customer | null = null;
Using null will stop the compiler from complaining when you pass the input through the async pipe (the async pipe returns something like T | null).
Also by doing that you'll have to make sure that you account for null values wherever you use that property, so fewer errors to deal with in production if something goes wrong let's say.
I am using the interface to define a type of variable and initializing that variable with a class that has some additional properties than the interface.
please refer to the code below:
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
username: string
constructor(name: string, id: number, username: string) {
this.name = name;
this.id = id;
this.username = username;
}
}
const user: User = new UserAccount("Suraj", 1, "srs");
// user.username
// I will not be able to access username in above line but when I console it, it will show the value assigned by the class constructor
console.log(user)
// output will be **UserAccount { name: 'Murphy', id: 1, username: 'srs' }**
My question is:
Why we are using Interface when we are initializing variables with the class?
And if we are using this then why it is not an error in the typescript when compiling?
And last if we are able to assign it (UserAccount { name: 'Murphy', id: 1, username: 'srs' }) then why can't we access user.username?
(Typing from phone, can add details later)
For starters you are not using the interface.
You defined a class and an interface that are completely unrelated and only have similar names.
To actually use the interface you’d have to do something like:
class UserAccount implements User {
…
}
Now answering your questions:
There many reasons one would create interfaces.
For instance you could share the interface with a different parte of the code letting it know what a UserAccount looks like without creating a dependency on the class itself.
Another user can be to define multiple interfaces such as “SoundPlayer” and “GraphicsPlayer”, then have a class implement one or both. Those classes could represent a music player by implementing “SoundPlayer” or a multimedia player by implementing both.
This also ensures that classes with similar functions “look the same”.
Not sure what you’re asking but I feel like you were expecting some sort of error that won’t occur since you’re not actually implementing the interface.
You can’t access user.username because it is part of the class UserAccount and not part of the interface.
I Kotlin if I have an interface like this:
interface User {
val name: String
val email: String
}
I can write an extension function like this anywhere in the code:
fun User.toUserDto(): UserDto {
TODO()
}
In Typescript if I have a similar interface:
export default interface User {
name: string;
email: string;
}
How can I augment it in a similar way? Is this a best practice in the language? Is there an alternative to this I don't know about?
You can augment classes like this, but it depends on the prototype property and there's no User.prototype (and no User value at all either).
You can also see this very long discussion. The explanation why the Kotlin/C# approach was rejected is here.
Interfaces in Typescript can only describe the shape of data, you cannot make instances of them or mutate them.
To write an extension function of an interface, you would need to implement the interface in a class, and add whatever you need on the class.
export class SomeUserClass implements User {
name: string;
email: string;
constructor(name, email) {
...
}
toUserDto() {
...
}
}
I have a Parent component. It injects data into two #Input() Child duplicate card components, showing html. In addition, there is difference class in parent, flagging difference between the two classes .
When displaying class members in html below, is there method to highlight items which have changed in, Without Modifying Child Components? Prefer not to inject difference logic in child card components. Would rather have the parent controller highlight differences, without overall logic leaking into child components.
All child components have same css class referring to member name, .city refers to city, .zipcode pertains to item zipcode,
Might need to create javascript function, and then applying in Angular in ngOnit, still researching, trying to apply this answer maybe? Generate dynamic css based on variables angular
export class PropertyLocation {
streetName: string;
streetType: string;
postdirectional?: string;
unitNumber?: string;
unitType?: string;
city: string;
state?: string;
zipcode: number;
effectiveStartDate: Date;
addressChangeReason?: AddressChangeReasonDto;
addressSource?: SourceOfAddressDto;
}
Difference class: feel free to restructure class in parent, to make solution easier if required
export class DifferenceClass {
ClassMember: string;
DifferentFlag: boolean;
}
derived from
function isSame(obj1:any, obj2:any): DifferenceClass {
let difference: DifferenceClass[];
for (const key in obj1) {
if (obj1[key] !== obj2[key]) {
differences.push(new DifferenceClass(key,true));
}
else
differences.push(new DifferenceClass(key,false));
}
return differences;
}
A custom Directive is helpful to separate some UI logic from the Component class.
Example:
#Directive({
selector: '[highlighter]',
})
#Input('streetName') streetName: string;
export class HighlighterDirective {
constructor(private host: ElementRef,
private rd: Renderer2) {}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['streetName'].currentValue != changes['streetName'].previousValue) {
this.rd.setStyle(this.host.nativeElement, 'color', 'red')
}
}
p.s. code is completely from my memory, could be some types, syntax errors. But you get the idea - Directive has access to the same inputs defined on the component:
<custom-component [streetName] highlighter></custom-component>
Then you can use ngOnChanges or other technique to distinguish when the property has changed its value and use Renderer2 (for the best practices), to apply direct changes to the DOM.