英文:
In Angular 14, how do I apply a function to a model field if HttpClient won't invoke its constructor?
问题
我正在使用 Angular 14。我的应用程序中有许多通过 HttpClient 的 GET 和 POST 请求返回我的模型的调用:
constructor(
http: HttpClient,
...
) {}
...
return this.http.post<MyObject[]>(`${this.entitiesUrl}search`, searchObj)
我注意到,当进行这些请求时,模型的构造函数没有被调用。我正在寻找一种在新构建的对象被应用程序的其他部分使用之前,对其中一些字段应用函数的方法。对 HttpClient 调用的结果应用 "map" 是一种选择,但由于有很多这样的调用,我将不得不多次应用相同的 "map",这似乎是一种浪费和不良的编程实践。
英文:
I'm using Angular 14. I have a number of calls in my application that return my model via HttpClient GET and POST requests:
constructor(
http: HttpClient,
...
) {}
...
return this.http.post<MyObject[]>(`${this.entitiesUrl}search`, searchObj)
I'm noticing that when these requests are made, the constructor of the model isn't called. I'm looking for a way to apply a function to some of the fields of my newly-built object before it is used by other parts of the application. Applying a "map" to the result of HttpClient calls is certainly an option, but there are so many, I would have to apply the same map dozens of times, which seems wasteful and poor coding practice.
答案1
得分: 1
在你的示例中,MyObject[]
只是一个提示,在实际情况中可能是像 JSON.parse(body) as MyObject[]
这样的形式。这也意味着你的模型中不能有任何方法,因为它们不会存在于反序列化后的对象上。
这真的取决于你想要实现什么目标。
如果你想要按名称、类型或值模式修改某些字段,或者注入相同的一组方法,那么拦截器是可以的。你甚至可以使用请求中的伪标头启用/禁用多个拦截器。如果你不使用拦截器,也没有计划使用拦截器,那么你可以将 HttpClient
封装到自定义服务中,然后简单地将 pipe(map(x => ...))
添加到 post/get 方法中。
示例:
https://stackoverflow.com/questions/46559268/parse-date-with-angular-4-3-httpclient
如果你想将反序列化后的对象转换为具体类的实例,那么以一种通用的方式是不可能的,你必须实现所有的映射、自定义构造函数、类注册表/工厂、判别器等等。
另一种选择是使用某种客户端代码生成工具(例如 OpenApi 或 Swagger)。通常有一种方法可以修改用于生成代码的模板,因此你可以添加自定义代码,用于进行日期转换,这样它就会自动应用于所有的服务。
英文:
In your example MyObject[]
is just a hint, in reality there is something like JSON.parse(body) as MyObject[]
. It also means that you can not have any methods in your model because they will not exist on deserialized objects.
It really depends on what exactly you want to achieve.
If you want to alter some fields by its name or type or value pattern, or inject the same set of methods then interceptor is ok. You can even have several interceptors and enable/disable them using fake headers in the request. If you don't use interceptors and don't plan to use interceptors then you can wrap HttpClient
into custom service that simply appends pipe(map(x => ...))
to post/get methods.
Example:
https://stackoverflow.com/questions/46559268/parse-date-with-angular-4-3-httpclient
If you want to convert deserialized objects into instances of concrete classes then it is not possible in some generic way, you will have to implement all the mapping, custom constructors, class registry/factory, discriminators and etc.
Alternative option is to use some sort of client code generation (e.g. OpenApi or Swagger). Usually there is a way to alter template that is used to generate code, so you can append custom code that does dates conversion and it will be automatically applied to all services.
答案2
得分: 1
你所要求的是获得TypeScript类型安全的好处,但你不想实现获得这些好处所需的繁重工作。
在TypeScript中,类型并不是严格强制执行的,除非经过额外步骤。你可以将一个接口应用到响应中,以获得点符号的好处,并在开发过程中通过IDE进行“隐式”类型检查。然而,你很快会意识到该接口的类型可以被API所违反... 这是一种“善意”的数据契约实现,你将其用作数据响应的隐式形状,并信任API不会违反它。
如果你想再进一步,你需要使用一个通过构造函数实现接口的类模式,以在创建类实例时“强制”进行显式类型检查... 据我所知,没有其他方法可以获得这个好处... 并且需要使用map
实现来循环遍历响应并映射数据,正如你已经指出的那样。
在调用API时,你会知道一些关于响应的信息,可以在响应期间用于将响应形状分类到相应的模型类中... 你可以通过使服务API/模型具体化来“抽象”这一点,但无论你选择哪种实现方式,你都必须以某种方式/某个级别实现分类/映射逻辑,以获得API响应的类型安全性。
在TypeScript中的真正类型安全只存在于运行时,通过将响应映射到实现构造函数的显式数据类型检查的类对象(运行时)而实现。
有关TypeScript行为的其他信息:
https://stackoverflow.com/questions/59135732/runtime-typesafety-in-typescript/59136620#59136620
英文:
What you are asking for is to get the benefits of TypeScript type safety, but you do not want to implement the heavy lift required to gain those benefits.
In TypeScript, types are not strictly enforced without extra steps. You can take an interface and apply it to a response to gain the benefits of dot notation, and "implied" type checking via the IDE during development, however, you will quickly realize that the types of that interface can be violated by the API... this is a "good faith" data contract implementation, and, you are using it as an implied shape to your data response and trusting the API to not violate it.
If you want to go a step beyond this, you will need to use a class pattern that implements an interface via constructor to "enforce" explicit type checking as the class instance is created... there is no other way that I know of to to gain this benefit... and it requires you to use a map
implementation to loop over the response and map the data as you have already pointed out.
There is information you will know at the time of the API call that can be used during the response for classifying the response shape into the respective model class... you can certainly "abstract" this away in to "several services" by making the services API/model specific, but in the end, no matter the implementation you choose, you will have to implement classification/mapping logic in some way/at some level to gain type safety for your API responses.
True type safety in TypeScript only exists at runtime by mapping responses to class objects (run time) that implement explicit data type checking via the constructor.
Additional info on how TypeScript behaves:
https://stackoverflow.com/questions/59135732/runtime-typesafety-in-typescript/59136620#59136620
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论