I'm learning HTTP as I go. Unsure how to move forward. I'm doing http calls only in my service.
Then I have a class, Teacher, here's some of the methods:
export class Teacher {
public addStudent(value: Student): void {
this.students.push(value);
}
}
I have a list component that lists teachers, and in that component, the user can click a teacher and move to a detail-page, where it takes user input and adds students to the teacher.
export class TeacherDetailComponent implements OnActivate {
teacher: Teacher;
constructor(public _service: Service, public _router: Router) { }
routerOnActivate(curr: RouteSegment): void {
let id = curr.getParam('id');
this._service.getById(id)
.subscribe(teacher => {
this.teacher = teacher;
});
}
addStudent() {
this.teacher.getStudents().push(new Student());
//what code here?
}
}
There is my headscratcher, how and where do I tell Angular that to update the data for the teacher when a new student is added!
In fact your question is related to component communication. I would create a shared service for this.
See this doc for more details:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
So I would create a service to notify the list component that a student is added or remove, so it can update the list accordingly. Here is a sample:
#Injectable()
export class StudentService {
userAdded:Subject<Student> = new Subject();
userDeleted:Subject<Student> = new Subject();
constructor(private http:Http) {
}
addStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((addedStudent) => {
this.userAdded.next(addedStudent);
});
}
deleteStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((removedStudent) => {
this.userRemoved.next(removedStudent);
});
}
}
So you can update your details component:
addStudent() {
let newStudent = new Student();
this.studentService.addStudent(newStudent).subscribe(addedStudent => {
this.teacher.getStudents().push(addedStudent);
});
}
In the list component:
this.studentService.addedStudent.subscribe(addedStudent => {
// do something
});
Related
I have an entity MultiChannel that has a OneToMany relation with SalesChannel.
The restriction is that MultiChannel cannot have 2 SalesChannels that have the same name property.
Initially I had created the Story below, but that will endup with SalesChannels that have the same name.
App/Tests/Story/MultiChannelStory
final class MultiChannelStory extends Story
{
public function build(): void
{
SalesChannelFactory::createMany(100);
MultiChannelFactory::createMany(50, function() {
return [
'salesChannel' => SalesChannelFactory::randomRange(1,3),
];
});
}
}
Then I created a SalesChannelStory as below:
App/Tests/Story/SalesChannelStory
final class SalesChannelStory extends Story
{
public function build(): void
{
$this->addToPool('name1', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[0]])->many(50));
$this->addToPool('name2', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[1]])->many(50));
$this->addToPool('name3', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[2]])->many(50));
$this->addToPool('name4', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[3]])->many(50));
}
}
The intention was to do something as below on MultiChannelStory, in somewhat pseudo code, so that
I could insert only uniquely named SalesChannel into MultiChannel:
App/Tests/Story/MultiChannelStory
final class MultiChannelStory extends Story
{
public function build(): void
{
SalesChannelFactory::createMany(100);
MultiChannelFactory::createMany(50, function() {
return [
'salesChannel' => $this->getUniqueNamed(),,
];
});
}
}
private function getUniqueNamed(): \App\Entity\Onetomanybi|\Zenstruck\Foundry\Proxy
{
// get up to 4 SalesChannel without the property `name` being repeated.
$items = [SalesChannelStory::getRandom('name1'), SalesChannelStory::getRandom('name2')];
return $items;
//return SalesChannelStory::getRandom('name1');
}
But that does not work.
Note that MultiChannel has to have at least one SalesChannel, up to as many SalesChannel exists, or 4 currently.
I think I have got a newbie question, but I searched over the Internet - no result.
So I'm calling a controller with a POST method with given parameters (weight and height) and I expect to receive a status code Ok(result) with an object inside it.
The method is called properly, I receive sth from the method, but the result is "undefined". I tried to tell the POST method to expect JSON results, by giving a header, but no result. I mean, I receive an Object, but I don't know why it's not mapped correctly and thus, the result is not shown as it should.
I was expecting, that response will be type Result, like in the class defined and I can freely read from it, but no.
That's the response I get
{"bmiClassification":0,"result":4.03,"summary":"To be done"}
Here is controller class I'm calling BMICalculatorController.cs
[ApiController]
[Route("[controller]")]
public class BMICalculatorController : ControllerBase
{
private readonly IBMICalculatorLogic _calculator;
private readonly ITest _test;
public BMICalculatorController(IBMICalculatorLogic calculator)
{
_calculator = calculator;
}
[HttpPost]
[Route("calc")]
public IActionResult Calculate([FromBody] ParametersDto parameters)
{
var result = _calculator.GetResult(parameters.Weight, parameters.Height);
return Ok(result);
}
}
}
Here is typescript component I'm working on:
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Component, Inject, OnInit } from '#angular/core';
import { ParametersDto } from '../models/ParametersDto';
import { Results } from '../models/Results';
#Component({
selector: 'app-bmicalculator',
templateUrl: './bmicalculator.component.html',
styleUrls: ['./bmicalculator.component.css']
})
export class BmicalculatorComponent implements OnInit {
public parameters: ParametersDto = new ParametersDto;
public result: number = 0.0;
public text: string = "Default text";
public results: Results = new Results();
constructor(private http: HttpClient, #Inject('BASE_URL') baseUrl: string) {}
ngOnInit(): void {
}
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters,
{ headers: new HttpHeaders().set('Content-Tye', 'application/json') }).
subscribe(response => {
this.results = response;
this.result = this.results.Result;
}, error => console.error(error));
}
}
Below is a class Result I expect to receive:
export class Results {
public Classification: BMIClassification = 1;
public Result: number = 0.0;
public Summary: string = "";
}
enum BMIClassification {
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Here is class of result that controller returns:
public class BMIResult
{
public BMIClassification? BMIClassification { get; set; }
public double Result { get; set; }
public string? Summary { get; set; }
}
and here is enum used in the class above
public enum BMIClassification
{
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Most probably, I messed up sth with in the Typescript, but I don't know where... Please give me any hint ! :)
[SOLVED]
I changed slightly the way how I read result and it worked :) Code below
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters).
subscribe(response => {
this.result = (response as any).result;
}, error => console.error(error));
}
}
I have a custom method decorator like this.
export function CustomDecorator() {
return applyDecorators(
UseGuards(JwtAuthGuard)
);
}
Inside the Custom Decorator, I want to get the Request Header but not sure how to get the Request Instance?
You won't be able to get the ExectuionContext object or the Request object in a class or method decorator, because these decorators are run immediately at the moment of import. What should be done instead is to make a SuperGuard that does have the ExecutionContext available to it. This SuperGuard should have all of the other guards injected into it via the constructor and depending on the header you should call/return the result from the guard called. Something like this:
#Injectable()
export class SuperGuard implements CanActivate {
constructor(
private readonly jwtAuthGuard: JwtAuthGuard,
private readonly googleAuthGuard: GoogleAuthGuard,
) {}
canActivate(context: ExecutionContext) {
const req = context.switchToHttp().getRequest();
if (req.headers['whatever'] === 'google') {
return this.googleAuthGuard.canActivate(context);
} else {
return this.jwtAuthGuard.canActivate(context);
}
}
}
I managed to access the execution context within decorator using Inject inside decorator's factory.
Here is my decorator that swallows errors produced by method and returns predefined value in case of exception.
import { Injectable, Scope, Inject, ExecutionContext } from '#nestjs/common';
import { CONTEXT } from '#nestjs/graphql';
#Injectable({ scope: Scope.REQUEST })
export class ExceptionsHandler {
public constructor(#Inject(CONTEXT) private readonly context: ExecutionContext) {}
private integrationsRequestErrors: unknown[] = [];
public handle(error: unknown): void {
// ADD error to context if necessary
this.integrationsRequestErrors.push(error);
}
}
export const ErrorSwallower = (options: {
serviceImplementation: string;
defaultValue: unknown;
errorMessage?: string;
}): MethodDecorator => {
const { defaultValue, integration } = options;
const Injector = Inject(ExceptionsHandler);
return (target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {
Injector(target, 'exceptionsHandler');
const originalMethod = descriptor.value;
descriptor.value = function (...args: unknown[]) {
const exceptionHandler = this.experiment as ExceptionsHandler;
try {
const result = originalMethod.apply(this, args);
if (result && result instanceof Promise) {
return result.catch((error: unknown) => {
exceptionHandler.handle({ error, integration });
return defaultValue;
});
}
return result;
} catch (error) {
exceptionHandler.handle({ error, integration });
return defaultValue;
}
};
};
};
and here is the code above put into action:
#Injectable()
export class ExampleService {
#ErrorSwallower({ serviceImplementation: 'ExampleClass', defaultValue: [] })
private async getSomeData(args: IGetSomeDataArgs): Promise<ISomeData[]> {
throw new Error('Oops');
}
}
I inherited an existing project in ASP.net/C# using the entity framework with code-first approach. I defined a new table and successfully did all the migration necessary so that the [myProject].[ExportFiles] is indeed visible as table in the database.
The following code works fine, except that it always creates double database entries. I am sure that the code is only called once, I checked that using breakpoints. Assume that my datbase context is called _db.
namespace myProject.Service
{
public void Export(int userId)
{
var currentExportTimestamp = DateTime.Now;
var currentUserId = 42;
var exportRecord= new ExportFiles // defining a new entry
{
FileName = cashflowFileName,
DateSubmitted = currentExportTimestamp,
UserId = currentUserId,
};
_db.ExportFiles.Add(exportRecord); // nothing written in DB yet
_db.SaveChanges(); // the entry is written to DB, but twice
};
};
The curious thing: The code above always writes two new records with increasing Ids, although it has only has a single reference, the ExportController.cs looks roughly as follows:
[Route("api/Export", Order = -1)]
[HttpPost]
public IHttpActionResult Export(int? selectedEntityId, DateTime? selectedDate)
{
var name = System.Web.HttpContext.Current.User.Identity.Name;
var userId = _userService.GetUserByName(name).Id;
if (selectedDate == null || selectedEntityId == null)
{
return BadRequest("Need to select appropriate data");
}
try
{
_export.Export(userId);
return Ok();
}
}
My debugging exercise revealed that this controller is already called twice, but I am not sure why.
The component MyView.tsx looks as follows:
export interface MyViewProps {
selectedDate: any,
selectedEntity: number,
exportStatus: boolean
setExportingStatus: (exportingStatus: boolean) => void;
selectDate: (date: any) => void;
selectEntity: (entityId: number) => void;
exportDispatch: () => void;
}
export class MyView extends React.Component<MyViewProps, any> {
constructor(props) {
super(props);
this.handleExport = this.handleExport.bind(this);
}
handleExport() {
this.props.setExportingStatus(true);
this.props.exportDispatch();
}
render() {
return (
<div>
<Button
type="primary"
onClick={this.handleExport}
disabled={this.props.exportStatus == true}
> Export
</Button>
</div>
);
}
}
Additional information needed
The data model reads:
namespace myProject.Entity.Models
{
public class ExportFiles
{
public int Id { get; set; }
public string FileName { get; set; }
public DateTime DateSubmitted { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
}
The currentUserId = 42 does exist as foreign key in the table User.
Edit
I figured that that the function is actually called twice, but I do not understand why.
Related questions
Entity Framework Creates New / Duplicate Entries for Associated Objects
Your code it's correct, checking whether the DateSubmitted value is the same in both of your duplicate values will tell you if indeed your record it's being duplicated by the .SaveChanges() method or if you are just calling the entire method twice.
Edit: Since you added the React Code i can see that you are registering to the event without needing it since you already are triggering it with the button click so this this.handleExport = this.handleExport.bind(this); is creating the duplicate requests
export class MyView extends React.Component<MyViewProps, any> {
constructor(props) {
super(props);
}
handleExport() {
this.props.setExportingStatus(true);
this.props.exportDispatch();
}
render() {
return (
<div>
<Button
type="primary"
onClick={this.handleExport}
disabled={this.props.exportStatus == true}
> Export
</Button>
</div>
);
}
}
Could you help me to create class 'MyClass'
class M should be Newable and implement IMyInterface
export interface IMyInterface<A>
{
SomeData : A;
}
export class MyClass<T,M inherits IMyInterface<T> and new() >
{
list = new Array<M>();
privete Creator()
{
const obj = new M();
obj.SameData = 'Hello data';
list.push( obj );
}
}
Think you're looking for something like this...
export interface IMyInterface<A> {
SomeData: A;
}
export class MyClass<T, M extends IMyInterface<T>> {
private list: M[] = [];
constructor(private constructorFunction: {new(): M; }) {
}
public add(item: T): void {
const obj = new this.constructorFunction();
obj.SomeData = item;
this.list.push(obj);
}
}
export class MyItem implements IMyInterface<string> {
public SomeData: string = '';
}
const collection = new MyClass<string, MyItem>(MyItem);
collection.add('Hello data');
I've tweaked your psuedocode so that it actually compiles and does what I think you were aiming for in the question. The important thing to note is that the types in TypeScript have no representation at runtime so you can't do new T(). Instead you need to pass in the constructor function for your class which has a type of { new(): M; }. You can then do a new X() with this value to get an object which extends your interface.