Le projet AngularPortfolioMgr peut importer les dépôts auprès de la SEC des sociétés cotées. La classe d’importation est FileClientBean et importe l’archive JSON à partir de « Kaggle ».
Les données sont fournies par année, symbole et période. Chaque ensemble de données JSON a des clés (appelées concepts) et des valeurs avec la valeur USD. Par exemple, le chiffre d’affaires annuel d’IBM en 2020 était de 13 456 $.
Cela permet deux types de recherches. Une recherche de données d’entreprise et une recherche de clés (concepts) sur toutes les entrées.
Les composants sous « Requête de l’entreprise » sélectionnent l’année de valeur de l’entreprise avec des opérateurs tels que « = », « >= » et « <=" (les valeurs inférieures à 1800 sont ignorées). La recherche de symboles est implémentée avec un composant de saisie semi-automatique angulaire qui interroge le backend pour les symboles correspondants. Les trimestres sont dans une composante sélectionnée des périodes disponibles.
Les composants sous « Éléments de requête Sec disponibles » fournissent au conteneur de composants Drag’n Drop les éléments qui peuvent être glissés vers le bas dans le conteneur de requête. « Term Start » est un terme mathématique qui signifie « parenthèse ouverte » en tant qu’opérateur logique. Le terme « fin » vient des mathématiques et fait référence à une parenthèse fermée. L’élément de requête est une clause de requête de la clé (concept).
Les composants sous « Sec Query Items » sont les termes de recherche dans la requête. Les composants de requête contiennent les paramètres de requête pour le concept et la valeur avec leurs opérateurs pour le terme de requête. Les termes sont créés avec l’enveloppe d’ouverture/fermeture entre parenthèses pour préfixer les ensembles de requêtes avec les opérateurs « et », et « ou », ou « ou non » et « non ou ».
Les paramètres de requête et la structure des termes sont vérifiés avec une forme angulaire réactive qui active le bouton de recherche s’ils sont valides.
Création du formulaire et de la requête de l’entreprise
La classe create-query.ts contient la configuration de la requête :
[FormFields.YearOperator]: « »,
[FormFields.Year]: [0, Validators.pattern(« ^\\d*$ »)],
[FormFields.Symbol]: « »,
[FormFields.Quarter]: [« »],
[FormFields.QueryItems]: fb.array([]), } , { validateurs : [this.validateItemTypes()]
} ); this.queryItemParams.formArray=this.queryForm.controls[
FormFields.QueryItems
] comme FormArray ; //delay(0) corrige l’exception « NG0100 : l’expression a changé après avoir été vérifiée » this.queryForm.statusChanges.pipe(delay(0)).subscribe(result => this.formStatus = result); } ngOnInit() : void { this.symbolFinancials.emit([]); this.financialElements.emit([]); this.availableInit.forEach((myItem) => this.availableItems.push(myItem)); this.subscriptions.push( this.queryForm.controls[FormFields.Symbol].valueChanges .pipe( debounceTime(200), switchMap((myValue) => this.symbolService.getSymbolBySymbol(myValue)) ) .subscribe((myValue) => (this.symbols = myValue)) ); this.subscriptions.push( this.configService.getNumberOperators().subscribe((values) => { this.yearOperators = values; this.queryForm.controls[FormFields.YearOperator].patchValue( values.filter((maValeur) => maValeur === « = »)[0]
); }) ); this.subscriptions.push( this.financialDataService .getQuarters() .subscribe( (values) => (this.quarterQueryItems = values.map((myValue) => myValue.quarter)) ) ); } » data-lang= »application/typescript »>
@Component({
selector: "app-create-query",
templateUrl: "./create-query.component.html",
styleUrls: ["./create-query.component.scss"],
})
export class CreateQueryComponent implements OnInit, OnDestroy {
private subscriptions: Subscription[] = [];
private readonly availableInit: MyItem[] = [
...
];
protected readonly availableItemParams = {
...
} as ItemParams;
protected readonly queryItemParams = {
...
} as ItemParams;
protected availableItems: MyItem[] = [];
protected queryItems: MyItem[] = [
...
];
protected queryForm: FormGroup;
protected yearOperators: string[] = [];
protected quarterQueryItems: string[] = [];
protected symbols: Symbol[] = [];
protected FormFields = FormFields;
protected formStatus="";
@Output()
symbolFinancials = new EventEmitter<SymbolFinancials[]>();
@Output()
financialElements = new EventEmitter<FinancialElementExt[]>();
@Output()
showSpinner = new EventEmitter<boolean>();
constructor(
private fb: FormBuilder,
private symbolService: SymbolService,
private configService: ConfigService,
private financialDataService: FinancialDataService
) {
this.queryForm = fb.group(
{
[FormFields.YearOperator]: "",
[FormFields.Year]: [0, Validators.pattern("^\\d*$")],
[FormFields.Symbol]: "",
[FormFields.Quarter]: [""],
[FormFields.QueryItems]: fb.array([]),
}
, {
validators: [this.validateItemTypes()]
}
);
this.queryItemParams.formArray = this.queryForm.controls[
FormFields.QueryItems
] as FormArray;
//delay(0) fixes "NG0100: Expression has changed after it was checked" exception
this.queryForm.statusChanges.pipe(delay(0)).subscribe(result => this.formStatus = result);
}
ngOnInit(): void {
this.symbolFinancials.emit([]);
this.financialElements.emit([]);
this.availableInit.forEach((myItem) => this.availableItems.push(myItem));
this.subscriptions.push(
this.queryForm.controls[FormFields.Symbol].valueChanges
.pipe(
debounceTime(200),
switchMap((myValue) => this.symbolService.getSymbolBySymbol(myValue))
)
.subscribe((myValue) => (this.symbols = myValue))
);
this.subscriptions.push(
this.configService.getNumberOperators().subscribe((values) => {
this.yearOperators = values;
this.queryForm.controls[FormFields.YearOperator].patchValue(
values.filter((myValue) => myValue === "=")[0]
);
})
);
this.subscriptions.push(
this.financialDataService
.getQuarters()
.subscribe(
(values) =>
(this.quarterQueryItems = values.map((myValue) => myValue.quarter))
)
);
}
Premièrement, il y a les tableaux pour les abonnements RxJs et les éléments disponibles et de requête pour Drag’n Drop. Le *ItemParams
contiennent les paramètres par défaut des éléments. Le yearOperators
et le quarterQueryItems
contiennent les valeurs déroulantes. Le tableau « symboles » est mis à jour avec des valeurs lorsque l’utilisateur saisit des caractères (dans le symbole) en saisie semi-automatique. Le FormFields
sont une énumération avec des chaînes de clés pour le groupe de formulaires local.
Le @Output() EventEmitter
fournit les résultats de la recherche et active ou désactive le spinner.
Le constructeur obtient les services nécessaires et le FormBuilder
injecté puis crée le FormGroup
avec le FormControls
et le FormFields
. Le QueryItems FormArray
prend en charge les formulaires imbriqués dans les composants du queryItems
déployer. Le validateItemTypes()
validator pour la validation de la structure de terme est ajouté, et le paramètre initial est ajouté. A la fin, les changements de statut du formulaire sont souscrits avec delay(0)
pour mettre à jour le formStatus
propriété.
Le ngOnInit()
La méthode initialise les éléments disponibles pour Drag’n Drop. Les changements de valeur de la saisie semi-automatique du symbole sont souscrits pour demander les symboles correspondants au backend et mettre à jour la propriété « symbols ». Le numberOperators
et les « trimestres » sont demandés au backend pour mettre à jour les tableaux avec les valeurs sélectionnables. Ils sont demandés au backend car cela permet au backend d’ajouter de nouveaux opérateurs ou de nouvelles périodes sans changer le frontend.
Le modèle ressemble à ceci :
<div class="container">
<form [formGroup]="queryForm" novalidate>
<div>
<div class="search-header">
<h2 i18n="@@createQueryCompanyQuery">Company Query</h2>
<button
mat-raised-button
color="primary"
[disabled]="!formStatus || formStatus.toLowerCase() != 'valid'"
(click)="search()"
i18n="@@search"
>
Search
</button>
</div>
<div class="symbol-financials-container">
<mat-form-field>
<mat-label i18n="@@operator">Operator</mat-label>
<mat-select
[formControlName]="FormFields.YearOperator"
name="YearOperator"
>
<mat-option *ngFor="let item of yearOperators" [value]="item">{{
item
}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label i18n="@@year">Year</mat-label>
<input matInput type="text" formControlName="{{ FormFields.Year }}" />
</mat-form-field>
</div>
<div class="symbol-financials-container">
<mat-form-field class="form-field">
<mat-label i18n="@@createQuerySymbol">Symbol</mat-label>
<input
matInput
type="text"
[matAutocomplete]="autoSymbol"
formControlName="{{ FormFields.Symbol }}"
i18n-placeholder="@@phSymbol"
placeholder="symbol"
/>
<mat-autocomplete #autoSymbol="matAutocomplete" autoActiveFirstOption>
<mat-option *ngFor="let symbol of symbols" [value]="symbol.symbol">
{{ symbol.symbol }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label i18n="@@quarter">Quarter</mat-label>
<mat-select
[formControlName]="FormFields.Quarter"
...