objects parsing in html component in angular - html

I want to use an object in #input parameter in the html.
for example:
<app-home [param]="user.salary"></app-home>
but the type of my user is something like that:
user:Person=new Employee();
my classes are:
export class Person {
constructor(public name:string){}
}
export class Employee extends Person {
constructor(public name:string,
public salary:number){
super(name);
}
}
how do I parse the user in the #input parameter to Employee?
I tried to do so:
<app-home [param]="(user as Employee).salary"></app-home>
but I get an error. so how can I do it?

If you want to pass the complete object, and as I may assume you're already defining it, change the param class to handle to object
class HomeComponent{
#Input() param: Employee;
}
then pass the object instead of a simgle property
<home-component [param]="user"></home-component>
This way you're getting the full component and you can now access and manipulate all it's properties.
If you have an object in home-component that you want to define by passing the user to it, try using a setter, like this
class homeComponent{
private _user:Employee;
#Input()
set param(data) {
this._user = data;
}
}
Or you can destructure it to handle easily each property and assign individually
class homeComponent{
private _user:Employee;
#Input()
set param({name, salary}) {
this._user = new Employee(name, salary)
}
}
If your User object is missing salary and you want to assign it after passing to the HomeComponent, you can try this
class homeComponent{
private _employee:Employee;
private _salary:number = 2000;
#Input()
set param(data) {
this._employee = new Employee({...data, salary: this._salary})
}
}
This way, you're getting your entire object, and trigger the setter to complete it's definition by adding the salary property;

Related

Dynamically loading json file by package name (Typescript)

I am trying to write a small browser game in Typescript. So to start, I have a Scenario class that would need to load characters' dialogues based on scenario names.
Here is how the Scenario class looks like:
export class Scenario extends Entity{
public characters: Character[] = [];
constructor(
public readonly scenarionName: string,
public readonly startDialogueId: string
) {
super();
}
public awake() {
this.loadCharacters();
}
private loadCharacters(){
this.characters = [];
// FIXME how can I dynamically load dialogues based on scenario name?
}
}
My idea was to have dialogues stored in JSON files, which would then be loaded based on the scenario name, which would also be a package name. Here how it looks:
In this case, I want to dynamically load 'npc.json' and 'player.json' into Scenario class based on the scenario name which in this case is a 'test'. In the future, there would be more scenarios and I would like to avoid hard coding.
Can this be done in such a way and if so, how?
EDIT: the game would run in the browser.
Use fs module to get the file names in a directory and read the contents of the files.
import fs from('fs');
export class Scenario extends Entity{
public characters: Character[] = [];
constructor(
public readonly scenarionName: string,
public readonly startDialogueId: string,
scenarioPath: string
) {
super();
}
public awake() {
this.loadCharacters();
}
private loadCharacters(){
this.characters = [];
const dialoguesDir = `${this.scenarioPath}/${this.scenarioName}/dialogues/`;
let dialogueContent;
fs.readdirSync(dialoguesDir).forEach(filePath => {
dialogueContent = fs.readFileSync(`${dialoguesDir}${filePath}`).toString();
// Do something with contents
});
}
}

Send Component Dropdown Values two levels up in Angular with Event Emitter

I am trying to send data between two unrelated components. I am trying to utilize Event Emitter Output and Address Service.
How to get the first Address Dropdown Event Emitter, to Pass Data to the Service? The service can then send data to the Receiver.
export class AddressDropdownComponent implements OnInit {
addresses: any[] = [];
#Input() addressDefaultItem: AddressDto;
#Input() selectedAddress: any;
#Input() TxtField: string = 'addressDescription';
#Output() selectedItemOutput = new EventEmitter();
constructor(private addressService:AddressServiceProxy ) { }
ngOnInit() {
}
statusSelectedItemChanged(e) {
this.selectedAddress = e;
}
Still Working on This Address Service
export class AddressService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Resource: following is only for parent-child, looking for unrelated 'grandfather' or 'sibling' cases
What is the best way to use nested components in Angular 4 and calling a method of a parent to child and vice versa?
So you have a source let's say ComponentA and some component on top of it several levels let's say ComponentV.
To connect them you need first to connect them via a service. If there is only one instance of each of them you can use a singletone service (has providedIn: 'root' in #Injectable decorator). However, if you can have multiple ComponentV which contain ComponentA you need to provide this service at top-level of hierarchy. In this case ComponentV must have providers: [SomeService] to create a new service instance when ComponentV is created.
Then you define some prop in SomeService to share data.
For example you end up with
// SomeService contents
...
// create a source
private dataSource$ = new Subject();
// Expose as observable so it's value can be changed
// only via exposed method (dataChanged)
data$ = this.dataSource$.asObservable();
// public API to pass new data
dataChanged(data) {
this.dataSource$.next(data);
}
...
Then you inject this service in ComponentA and define a function to emit data change.
// ComponentA contents
...
constructor(private readonly someService: SomeService) {}
onDataChange(data) {
// here we notify about data change
this.someService.dataChanged(data);
}
...
Then you need to subscribe to the observable in top level component
// CompoentV contents
...
constructor(private readonly someService: SomeService) {
// here we subsribe to changes and use whatever handler we need
this.someService.data$.subscribe(data => {
// some logic goes here or pass this data to a method
});
}
...
This allows one to share some state or events between unrelated or loosely related components.

Property getter calling function can't be binded?

I have a model class with a property that returns a value by calling a method, but when i try to bind that property, there is result on the page, but also no error occuring.
export class TestClass {
testProperty: string = this.getString();
getString() {
return 'hello';
}
}
in html:
{{model.testProperty}}
Does Typescript / Angular not support this? What is the common way to do it?
This is a simple enough class. What you could do is initialize testProperty as null or by a default value and in ngOnInit() assign the returned value from the function.
import { Component, OnInit } from '#angular/core';
export class TestClass implements OnInit {
testProperty: string = null;
ngOnInut() {
this.testProperty = this.getString();
}
getString() {
return 'hello';
}
}
The ngOnInit() is a lifecycle hooks and runs when the component is initialized.
public get testProperty(): string {
return 'hello'
}
If you use the 'get' after the public, it allow the function to be called as it would be a normal variable. I think you shouldnt even need the 'model.'

Get class name in constructor

export class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
As you can see above, I'm writing InvalidCredentialsError twice. Is there a way to somehow get the class name already in the constructor method and set it? Or does the object have to be instantiated?
In browsers with native ES6 class support, this.constructor.name will display the InvalidCredentialsError. If you transpile the code with Babel it will show Error.
Without Babel (use on Chrome or another browser that supports class):
class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
console.log(this.constructor.name);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
const instance = new InvalidCredentialsError('message');
With Babel:
class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
console.log(this.constructor.name);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
const instance = new InvalidCredentialsError('message');
this.myName="literallycopiedclassname"
Is not the same as
this.myName=this.constructor.name
Look at this:
class B extends Error {
constructor() {
super();
this.myName="B";
}
};
class D extends B {constructor() {super();}};
let o=new D(); console.log(o.myName);
And this:
class B extends Error {
constructor() {
super();
this.myName=this.constructor.name;
}
};
class D extends B {constructor() {super();}};
let o=new D(); console.log(o.myName);
As I know, you cannot access static class properties from within constructor of that class an easy way, except when explicitly referencing the class:
this.myName=B.name;
However, you can access static properties of the outermost class of the constructors super()-chain (the class of object being constructed) (and of its ancestors) using this.constructor. If it is, what you want, enjoy the this.constructor.name construction.
If you want to record the class name from within the constructor defined by that class, you need to explicitly reference the class, but then you need to write it's identifier once more as well. However, it doesn't need to be a string literal, as shown above. If the reason for your question is that you dislike using string literals where not necessary and/or you will avoid maintaining all the identifier-names and literals when renaming the class(es), you can try the following:
If you write
class B extends Error {};
Then B is an identifier refering to the class being defined, accessible in the class' body, AND that class' static property "name" (B.name) is set to it's literal representation (B.name="B"). Additionally, if written this way, it behaves like this:
B=class B extends Error {};
So you can reference the class with B from outside the class' body as well. But you can also write
C=class B extends Error {};
Than you can reference the class with C from outside, with B from inside and the "name" property becomes "B". It shows, that the "internal" name can differ from the name of variable holding the reference to the class. You can name it at will and there is no need to rename it:
const C=class ThisClass extends Error {
constructor() {
super();
this.myName=ThisClass.name;
}
};
let o=new C();
The drawback is, that your class will have the internal name ThisClass. It may or may not be wrong, all classes can have the same internal name without interferring, when you don't use it for some (e.g. debug) purposes. After all, the internal name can be reassigned (again, to whatever you want):
Object.defineProperty(C,"name",{value:"C", configurable:true});
However, you have to write it as a string literal again. But there is a workaround:
You can save the reference to the class whereever you want:
const cName="YourClass";
const classes={};
classes[cName]=class ThisClass extends Error {};
All put together:
const cName="YourClass";
const classes={};
Object.defineProperty (
classes[cName]=class ThisClass extends Error {
constructor() {
super();
this.myName=ThisClass.name;
}
},
"name",
{value:cName, configurable:true}
);
class D extends classes[cName] {
constructor() {super();}
};
let o=new (classes[cName])();
console.log("YourClass myName", o.myName);
console.log("YourClass constructor", o.constructor.name);
let od=new D();
console.log("D myName", od.myName);
console.log("D constructor", od.constructor.name);
Now there is only one place, where the class name is specified, as string literal. And you don't need to rewrite the name in new...

Can you implement an interface on a Linq2Sql class?

I have an interface called IAddress, and a class called Address that handles street, city, state/province, postal code and country. I have a couple of Linq2Sql classes that has all the address information and would like to implement the interface IAddress, and pass that in to the constructor for Address that would the load the property values.
Is it possible have a Linq2Sql class impment and interface through the partial class that I created for it? Thanks in advance!
Additional comments
In my class I have a property called MailToStreet, I want that to map to IAddress.Street. Is there a way to do this in the partial class?
Solved
Thanks StackOverflow community! It was a snap! Here is my final code:
public partial class Location : IAddress
{
string IAddress.Street
{
get { return this.Street; }
set { this.Street = value; }
}
string IAddress.City
{
get { return this.City; }
set { this.City = value; }
}
string IAddress.StateProvince
{
get { return this.StateProvince; }
set { this.StateProvince = value; }
}
string IAddress.PostalCode
{
get { return this.PostalCode; }
set { this.PostalCode = value; }
}
string IAddress.Country
{
get { return this.Country; }
set { this.Country = value; }
}
}
LinqToSQL classes are partial classes, so you can have an additional file that implements the interface for the LinqToSQL class.
Just add this to a new file, using the same class name as your LinqToSQL class:
public partial class LinqToSqlClass : IFoo {
public void Foo() {
// implementation
}
}
If your LinqToSQL class already implements the necessary proporties you should be able to only include the interface declaration.
To answer the comment about using a differently-named LinqToSQL property to implement the interface, you can use the syntax above and just call the LinqToSQL property from the interface property, or to keep things a bit cleaner, use explicit implementation:
public partial class LinqToSqlClass : IFoo {
void IFoo.Foo() {
return this.LinqFoo(); // assumes LinqFoo is in the linq to sql mapping
}
}
By using this syntax, clients accessing your class will not see a redundant property used only for implementing an interface (it will be invisible unless the object is cast to that interface)