Originally published at blog.florimondmanca.com on Sep 4, 2018.
In a previous post, I wrote about best practices in REST API design. These were mo...
For further actions, you may consider blocking this person and/or reporting abuse
Interesting point. I never thought that the generic type you could pass to
.get()
,.post()
, etc, could be used by Angular to perform the conversion. Do you have examples of that being used?The case for adapters is also handling complex conversions, such as building nested objects and the like. For example, what if
GET: /courses/
returned course objects with astudents
field, representing a list ofStudent
objects? Can generic type conversion handle that?Also, how can generic type conversion decide to build a
Date
object out of thecreated
field (a string)?Happy to discuss further. :)
That code snippet works like a charm i’m using it currently it been like that since angular 5 or 4 not sure, your right about the date thingy but if you send it from backend as a date type. You’ll get it as a date type too in angular.
Cool! Glad to hear simple type conversions work.
To my knowledge there is no
Date
type in JSON, though, so that still means the model-adapter pattern is needed in more complex use cases.In typescript/JavaScript there is obviously, you’ll access it like a normal property Obj.dateProperty
the generic type you are able to pass to an http get or post, etc... its merely to allow typed responses. The adapter is still needed if your model objects contain any methods, etc. So for instance if you have a method such a getFullName() within your User model, this wont be available for the objects returned by http.get. its still necessary to do a map in order to actually create the User objects based on the data received. Hope this clarifies a bit
angular.io/guide/http#requesting-a...
The company I work for, we use a static method to construct that instead of a service:
And then we can use it as follows:
Do you see any of these approaches being better than another for some reason? They produce exactly the same result, but yours utilizes Angular's DI, mine does not.
It's a very clever alternative implementation of this pattern. I like how this approach binds the adaptation code to the model class. Also saves an import statement, and the DI boilerplate. :-) I'd definitely try this out in my next Angular projects!
Hi Florimond Manca,
I am using the above mentioned method in my current project. Now I'm stuck in one requirement where the value of one variable should change according to the some conditions .
example :-
export class Course {
constructor(
public id: number,
public code: string,
public name: string,
public created: Date,
) { }
static adapt(item: any): Course {
return new Course(
item.id,
item.code,
item.name,//Change the name to "john doe" if item.id=0 and item.code = john
new Date(item.created),
);
}
}
Is there any option for me to add a function which would perform this action?
Indeed. Map statement reads really nicely. 👌
Interesting approach to map the values to a typescript object. I currently use something like
to return the correct type and also be able to set default values in the class, but this doesn't decouple the code as nice as your Adapter. Might be worth a try, thank you :)
Thanks for sharing your approach :) I also used it in some cases when I was sure the fields wouldn't change much; but as soon as they did, I had to resort to an adapter.
(Also, I've just learnt about TypeScript
Partial
, thank you ✌️)Just be careful with Object.assign() in that constructor. While you're telling Typescript that you'll be receiving a Partial as an argument, that is not validated in runtime. So you might have a Person object with more (or less) properties than expected, should your back-end misbehave for some reason.
For advanced front-end developers there is a way of generate the adapter part from openApi (swagger) specification. Even a beginner can generate angular project on page editor.swagger.io/. It can take part in developer flow so you are warned by typescript compiler if the api has changed.
This is similar to what we do at my place of employment.
We build the back-end service in Java, using Spring and Spring/Swagger annotations.
We take the generated front-end controllers and models from Swagger and then, as an example, for a 'get' request, send the return json object to an NGXS state class that we build out and use the model in the state class to set the respective states.
Also transform.now.sh/json-to-io-ts/ or the big list of similar tools here
I have a question, when we change the name of the field from "name" to "label", wouldn't be better if we refactor our front-end code? I mean, it's better if there's a corrispondence between front-end model and back-end model, and if for example I want to update a course sending a POST request, should I provide another adapter to reflect the changes?
Refactoring is an on-going process, and this kind of slight difference between the backend and the frontend is certainly worth fixing at some point. The key point here is that you don't have to refactor right now — contrary to not using an adapter, where all your code base might break. You can just make that one-line change, the code will work and the frontend team can refactor later. 👍 That's decoupling in every sense of the word.
As for the POST request, very good point! What to do when you need to adapt data "the other way around"? Well, I think it's the same idea. You can implement the reverse operation: Model instance -> API data. I generally do it on another method on the adapter, like
.encode()
or.serialize()
. This way, we keep the adapter as a single interface between the external and internal representation (now in both directions).Thank you for the reply!
Hi Florimond,
this is great but can you add short description (maybe here in comment section) how to use your services in other components for the n00bs? Thanks! :)
Hey! You're right, I suppose it would be very useful to see how the
CourseService
can be used in practice to fetch and display data in a component. I'm actually writing a follow-up post for that! Will link to it here once it's published. :-)Will cross-publish here soon, but I've just published it to my blog: Consuming APIs In Angular: Displaying Data In Components. 🙌
Wow! Thanks! That was really fast! :)
Great article, thanks!
Can you help me understand the pros and cons of the following approach, compared to the adapter approach?
In the following code (which I did not write), you can see the author is mapping an unknown object to the Hero class. If the API changes, they can simply change the constructor mapping, and keep the Hero properties as is.
Why not just put all of that adapter logic in the model's constructor?
The possibilities of Angular built-ins performing similar work aside, this was a great example of the practical application of sound design patterns and SOLID principles. Nice job!
Thanks! Really appreciated. Do you know of any Angular built-in doing this kind of thing — aside from the generic type doing automatic conversions, which was mentioned in another thread?
No ... I was just referring to he conversation earlier in this thread. Framework features are great and should be leveraged when available/known, but sound design practices are always applicable.
As far as I understood adapter doesn't really validate types, it only checks the presence of fields in the structure. You can validate actual input with something like io-ts or sarcastic. I wrote a small article about IO validation if you interested.
Yep, this pattern won’t validate the JSON data you receive. In fact, unless you implement error handling it will probably result in errors if the data doesn’t fit the adapter (eg providing a value that cannot be converted to a
Date
).It’s simply that this pattern is only applicable if you know exactly the schema of the data you’re going to receive (that is, you built the API or have thorough documentation about its data format).
So good point for mentioning this limitation and providing resources to implement actual validation. :)
Good work, I'll be doing some refactoring with these ideas in mind. Thanks!
Congrats! You are getting very close the the Ember Data way of doing this:
guides.emberjs.com/release/models/
Thank you for this! Very well written and easy to understand.
This is really great, I have been worrying something similar, but hadn't thought to make it a reusable interface for models.
Imo, CourseService should receive an
Adapter<Course>
and not the concrete CourseAdapter.