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.
Related
I have a ReactNative app and I am trying to write a test using Jest. My test need to use classes from a native component (react-native-nfc-manager) and a class I need is declared like this
export interface TagEvent {
ndefMessage: NdefRecord[];
maxSize?: number;
type?: string;
techTypes?: string[];
id?: number[];
}
and if I try to use this definition directly like
const event = new TagEvent();
I get this error
TypeError: _reactNativeNfcManager.default is not a constructor
I am coming from Java worlds so I think to myself - of course it cannot instantiate an interface, it needs a class so I write a test instance for this interface:
class TestTagEvent implements TagEvent {
ndefMessage: NdefRecord[] = []
maxSize?: number;
type?: string;
techTypes?: string[];
id?: number[];
}
But the problem seem to be different since it does not work either:
TypeError: _TestTagEvent.TestTagEvent is not a constructor
What am I missing?
You can't construct an object using an interface. An interface is just a set of properties that can be used to define required/available properties of variables.
To use the interface to initialize a variable, you want to do this:
const eventVar: TagEvent;
If you want to use TagEvent as a instantiable object, you need to define it as a class, not an interface. Something like this, where TagEventProps is the TagEvent interface you defined:
class TagEvent {
constructor(props: TagEventProps) {
this.ndefMesssage = props.nDefMessage;
//etc...
}
}
After further experiments problem has been solved by adding 'export' to the class. Admittedly, the original error description is absolutely not matching the root cause.
export class TestTagEvent implements TagEvent {
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.
I try to dynamic create variables in class to store values and use it in ngModel and other placese.
I know, that I can assign value to variables in ngOnInit() like this
export class Component implements OnInit{
name: string;
ngOnInit(){
this.name = 'John Doe';
}
}
But I have some problem - I get my fields from server API and don't know what and how many items I get. I can only parsing server response and assign value to new variables after get it.
I can't do it like this (TS2540: Cannot assign to 'name' because it is a constant or a read-only property.)
export class Component implements OnInit{
ngOnInit(){
name = 'John Doe';
}
}
How can I assign new fields to my class in ngOnInit() or maybe in other place? (I think I can do it in constructor, but documentation say i shouldn't use it with Observable call to API and other difficult things)
You can use something like this to accomplish that:
ngOnInit() {
this['prop'] = 'value';
}
Here is a link to a working example: https://stackblitz.com/edit/dynamic-class-props
You can add an indexer to the class to be able to use any property name and not get a compiler error:
export class Component {
[name: string]: any;
ngOnInit(){
this["name"] = 'John Doe';
this.nameaa = "dd"
}
}
You should take care though, this means you can misspell property names and the compiler will not issue any error.
While reading about #Input() and #Output() I have found that we can use an alias instead of using the property name for the decorators.
Example
class ProductImage {
//Aliased
#Input('myProduct') product: string;
//Un-aliased
#Input() product: string;//not aliased
}
HTML
//Aliased attribute
<SomeComponent [myProduct] = "Product Name"></SomeComponent>
//Un-aliased attribute
<SomeComponent [product] = "Product Name"></SomeComponent>
The official Angular documentation says:
Sometimes we want the public name of an input/output property to be different from the internal name.
This is frequently the case with attribute directives. Directive consumers expect to bind to the name of the directive. For example, when we apply a directive with a myClick selector to a tag, we expect to bind to an event property that is also called myClick.
And This tutorial briefly explains it:
an alias let's me override the property name to be the alias instead of the original property name
Other than that I have not been able to find anything else on aliasing #Input() and #Output() on SO or through Google.
Things I would like to know are:
What does 'aliasing' attempt to achieve?
Is 'aliasing' something that we should be using regularly?
It's like your name and your nickname.
Inside your family you might be called Nic, where as , outside your family in order for other to know you legally , you should be called Nicolas.
So, if we consider the class of your component , the inside of your family :
#Component({
selector:'my-component'
})
export class MyComponent{
#Output('nicolas') nic = new EventEmitter<any>();
someFunction(){
// because we're inside the family ( inside this class ), instead of nicolas , we can call nic like this :
this.nic ...... ( which you'd normally emit an event ).
}
}
But from outside, you're still nicolas , so when someone want's to call you ( use that event emitter ) it should use nicolas;
<my-component (nicolas)="onNicolasChanged($event)"></my-component>
Note that we can't do this :
<my-component (nic)="onNicolasChanged($event)"></my-component>
This is aliasing that variable.
Also , please read my answer here :
What does 'aliasing' attempt to achieve?
it simply renames the input/output variable as per your needs. For example, if there is a hero object and when it is selected it is passed to another component. In that case it would be appropriate to call it selectedHero . Aliasing simply achieves that.
#Input('selectedHero') hero: string;
Is 'aliasing' something that we should be using regularly?
Depends on the context you are working on. Its not a necessity. For example it can be used to avoid conflicts in variable names , or for readability of the code.
Actually #Input is used when we are sending data of a parent component to child component and #Output is vice-versa. Example is
parent.component.html
<p> Child first name is {{childfirstname}}</p>
<input placeholder="First Name" type="text" [(ngModel)]="firstname">
<button mat-button (click)="send()">Click Parent</button>
<app-child
[firstname]="firstname"
(sendfirstname)="reciveFirstname($event)"
*ngIf="flage">
<app-child>
ParentComponent.ts
import { Component } from '#angular/core';
#Component({
selector:.......
)}
export class ParentComponent {
flage: boolean = false;
firstname: string;
childfirstname: string;
}
send() {
this.flage = true;
}
reciveFirstname(firstname) {
this.childfirstname = firstname;
}
Child.component.html
<p> Parent first name is {{firstname}} <br>
parent last name is {{lastname}} </p>
<input placeholder="First Name" type="text" [(ngModel)]="firstnamechild">
<button mat-button (click)="send">Send Parent</button>
child.component.ts
import { Component,Input, Output, EventEmitter } from '#angular/core';
#Component({
selector:'app-child',
........
})
export class ChildComponent {
firstnamechild: string;
#Input() firstname: string;
#Output()
sendfirstname: EventEmitter<string> = new EventEmitter<string>();
constructor() {}
send() {
this.sendfirstname.emit(this.firstnamechild);
}
}
A good reason to use it is if you ever need to change the variable name in the component, you don't need to change it in your template because it's using the alias. It's a simple way to prevent your code from breaking if another dev ever changes that variable name.
Following a post regarding creating objects dynamically in TypeScript, I have the following code used as a factory to create an object from its name:
public createComponent(context: Object, componentName: string): ComponentRef<ComponentBase> {
this.viewContainer.clear();
var instance =
Object.create(context[componentName].prototype); // <-- fails
let componentFactory =
this.componentFactoryResolver.resolveComponentFactory(instance);
return <ComponentRef<ComponentBase>> this.viewContainer.createComponent(componentFactory);
}
I'm not entirely convinced I understand this window[suchAndSuch] syntax: what does it mean? Can't find any documentation for it.
In any event it seems that window[myClassName] is undefined, even though the class in question is defined in the same file. I have looked at the Javascript transpiled from the TypeScript, and the class in question is in scope.
I think.
Help!
-- UPDATE --
I have this code, which is part of an Angular 2 application, that is calling the above method to try to get an instance of a component, injectables and all:
export class CustomUserComponent implements OnChanges {
#Input() componentType: string;
#ViewChild(ComponentDirective) componentAnchor: ComponentDirective;
ref: ComponentRef<GalleriaComponentBase>;
ngAfterViewInit() {
this.ref = this.componentAnchor
.createComponent(window, this.componentType);
}
}