So i have a complex date model coming from the server to feed my Angular2 component. Following shows a small part of the template for this component:
<div>
<span>{{Person.Address.City}}</span>
<input type="text" [(ngModel)]="Person.Address.City" />
</div>
The Address might be null or undefined depending on the data that's returned from the server. I know that elvis operator will save me from one error:
<span>{{Person?.Address?.City}}</span>
but unfortuantely it won't save me for [(ngModel)]="Person.Address.City" as there is no elvis defined for that. You'll get a parser error if you do something like [(ngModel)]="Person?.Address?.City" and if you don't then you'll get the null exception.
Here are two simplest variation of data, although there can be a lot more:
{
Name:'sam',
LastName: 'jones',
Address: {
Street: '123 somewhere',
City: 'some land'
State: 'SL'
}
}
or...
{
Name:'sam',
LastName: 'jones'
}
Since the responsibility of handling these scenarios should be on Angular2 rather than the server that provides the data, due to separation of presentation and business logic, how would I handle the scenario(s)?
So as it turns out, you either maintain 3 models, one for front-end (Angular2), one for middle tier (ASP.NET, JAVA, etc...) and another for your data model. Or you could make sure, as i did, that your middle-tier model will not have a null complex object. This means initializing complex properties inside your model and getting away with 2 models.
Also to note, if you have don't or can't modify your middle-tier model, then your only option is to create a compatible model for your front-end (Angular2) and merge as necessary once data has returned from API, service, etc..
At the service level you can add a blank object for Address. Right after receiving a response.
This way you fix the problem at it's source for entire application.
Here is simple solution with RxJS:
this._http.get(url)
.map(res => res.json())
.map(res => {
if (res.Address === null || res.Address === undefined) {
res.Address = {}
}
return object;
});
You might also want to consider removing the Address object if it is blank, right before saving it.
Related
How to join column, that when i launch my function, he return me only columns name instead of entity_columnName.
I'm using TypeORM, and i try this;
const data = this.conn.getRepository(User).createQueryBuilder('user');
data.leftJoinAndSelect('user.orders', 'orders');
data.getRawMany();
but return me:
firstName: ...
lastName: ...
age: ...
order_name: ...
order_price: ...
instead of:
firstName: ...
lastName: ...
age: ...
name: ...
price: ...
can someone tell me how do this? thanks for any help
How are your User and Order entities defined?
If you define a relation (e.g. OneToMany) with the eager: true option, then TypeORM will automatically include the related entities when you query using the repository *find methods. It won't do this when you use the QueryBuilder, where you have to add them such as with leftJoinAndSelect() (as you have done).
An example from an Invoice entity that has OneToMany line items:
#OneToMany(
() => InvoiceLineItem,
(item: InvoiceLineItem) => item.invoice,
{ eager: true }
)
items: InvoiceLineItem[]
Per the example, if I were then to find() or findMany() invoices, then the related objects, items will be included as well because eager: true.
This behaviour might translate well to your situation with users and orders.
Also be aware of the differences between getMany() and getRawMany() when using the query builder.
If you use getMany() then TypeORM will automagically give you entities back (e.g. you'd get an instance of User with property orders that is an array of Order instances). The property names will be correct.
Since you added the NestJS tag to your question, also understand serialization:
https://docs.nestjs.com/techniques/serialization
There is a built-in ClassSerializerInterceptor that comes with NestJS that you might find useful.
In your controller you can decorate the class or any of its methods e.g.
#UseInterceptors(ClassSerializerInterceptor)
This will transform the response to JSON, and will apply the rules specified with class-transformer decorators on the entity/DTO class.
If you were to use this interceptor, the response sent to the client will have your desired property names.
If you really want to modify the response that your client gets back, you can also look into writing your own Interceptor.
When reading the redux docs I found this:
Still, you should do your best to keep the state serializable.
Don't put anything inside it that you can't easily turn into JSON.
So my question is, what's the benefit of keeping state serializable?
Or, what difficulties I may have if I put non-serializable data into store?
And I believe this is not unique to redux - Flux, even React local state suggest the same thing.
To make me clear here is an example. Suppose the store structure is like this.
{
books: {
1: { id: 1, name: "Book 1", author_id: 4 }
},
authors: {
4: { id: 4, name: "Author 4" }
}
}
This should all looks good. However when I try to access "the author of Book 1", I have to write code like this:
let book = store.getState().books[book_id];
let author = store.getState().authors[book.author_id];
Now, I'm going to define a class:
class Book {
getAuthor() {
return store.getState().authors[this.author_id];
}
}
And my store will be:
{
books: {
1: Book(id=1, name="Book 1")
},
...
}
So that I can get the author easily by using:
let author = store.getState().books[book_id].getAuthor();
The 2nd approach could make the "book" object aware of how to retrieve the author data, so the caller does not need to know the relation between books and authors. Then, why we are not using it, instead of keeping "plain object" in the store just as approach #1?
Any ideas are appreciated.
Directly from the redux FAQs:
Can I put functions, promises, or other non-serializable items in my store state?
It is highly recommended that you only put plain serializable objects, arrays, and primitives into your store. It's technically possible to insert non-serializable items into the store, but doing so can break the ability to persist and rehydrate the contents of a store, as well as interfere with time-travel debugging.
If you are okay with things like persistence and time-travel debugging potentially not working as intended, then you are totally welcome to put non-serializable items into your Redux store. Ultimately, it's your application, and how you implement it is up to you. As with many other things about Redux, just be sure you understand what tradeoffs are involved.
Further reading:
What is time travel debugging?
Adding to what #Timo said , If you want to setup relation between 2 states in your state tree and use computed values, reselect is the best suitable fit for that scenario. It allows to creareselectors which can be used to define computed states. In your case author can be created using a selector on top of book. https://github.com/reactjs/reselect
#timo 's answer is correct. In addition, I recommend a library called Redux-ORM to work with normalized/relational data in your Redux store. See my recent comment at Dealing with data consistency in a very large store in React + Redux SPA SaaS for links to more information.
Adding this because you asked for a suggestion. If you only want to create an object out of your class then you can simply create a function that returns an object. In your Book class it could be something like
function newBookObject({id, name}) {
return {id, name}
}
And your store would look like
{
books: {
1: newBookObject({id: 1, name: "Book 1"})
},
...
}
The object being returned can't contain any function's in it though, it should be just a plain static object with pure data and nothing else. You can't serialize something that isn't pure data.
I have an Angular + Breeze + WebApi(EF) setup which works very well when querying for data. The breezeProvider is set for camelCase on the client by default, and PascalCase on the server side.
bp.NamingConvention.camelCase.setAsDefault();
The trouble I'm having is when pushing new data over signalR to my app. The data arrives PascalCased from the .NET Stack (Breeze explicity says to not mess with the casing on the server). I then use the standard factory to create a new entity and try to initialize it with the passed values. Since the initialier hash is all PascalCased, most of the properties fail to initialize properly. Is there a way to tell Breeze that it should convert this data the same way that it does when querying? I have not yet been successful. Basically, I just want Breeze to treat this the same as it treats all of the data it receives.
function onSignalRData(call){
var callType = manager.metadataStore.getEntityType("Call");
// call json from server is like { Id: 1, Name: "Paul", IsActive: true }
// I think Breeze expects { id: 1, name: "Paul", isActive: true }
var newCall = callType.createEntity(call);
}
Any suggestions? I see Breeze mentions manually defined naming conventions, but again I'm not doing anything exotic here, just doing the same thing that Breeze does on queries but in a creation initializer.
This may not be the best solution, but it seems to work for me. I used the same method as formatting json data to be camelCased and reparsed the data with a reviver function to camelCase it. I'm surprised that there's not a pipeline block in breeze entityManager.createEntity(...) that can handle this.
function reviver(key, val) {
if (key)
this[key.charAt(0).toLowerCase() + key.slice(1)] = val;
else
return val;
}
var parsed = JSON.parse(myjson, reviver);
Is there a way to configure a JsonRestStore to work with an existing web service that returns an array of objects which is not at the root-level of the JSON response?
My JSON response is currently similar to this:
{
message: "",
success: true,
data: [
{ name: "Bugs Bunny", id: 1 },
{ name: "Daffy Duck", id: 2 }
],
total: 2
}
I need to tell the JsonRestStore that it will find the rows under "data", but I can't see a way to do this from looking at the documentation. Schema seems like a possibility but I can't make sense of it through the docs (or what I find in Google).
My web services return data in a format expected by stores in Ext JS, but I can't refactor years worth of web services now (dealing with pagination via HTTP headers instead of query string values will probably be fun, too, but that's a problem for another day).
Thanks.
While it's only barely called out in the API docs, there is an internal method in dojox/data/JsonRestStore named _processResults that happens to be easily overridable for this purpose. It receives the data returned by the service and the original Deferred from the request, and is expected to return an object containing items and totalCount.
Based on your data above, something like this ought to work:
var CustomRestStore = declare(JsonRestStore, {
_processResults: function (results) {
return {
items: results.data,
totalCount: results.total
};
}
});
The idea with dojo/store is that reference stores are provided, but they are intended to be customized to match whatever data format you want. For example, https://github.com/sitepen/dojo-smore has a few additional stores (e.g. one that handles Csv data). These stores provide good examples for how to handle data that is offered under a different structure.
There's also the new dstore project, http://dstorejs.io/ , which is going to eventually replace dojo/store in Dojo 2, but works today against Dojo 1.x. This might be easier for creating a custom store to match your data structure.
I got nested JSON data from the server like this:
{
name: "Alice",
profile: {
something: "abc"
}
}
and I have the following model:
App.User = Ember.Object.extend({
name: null,
profile: Ember.Object.extend({
something: null
})
})
If I simply do App.User.create(attrs) or user.setProperties(attrs), the profile object gets overwritten by plain JS object, so currently I'm doing this:
var profileAttr = attrs.profile;
delete attrs.profile
user.setProperties(attrs); // or user = App.User.create(attrs);
user.get('profile').setProperties(profileAttrs);
It works, but I've got it in a few places and in the real code I've got more than one nested object, so I was wondering if it's ok to override User#create and User#setProperties methods to do it automatically. Maybe there's some better way?
Based on your comment, you want the automatic merging behaviour you get with models (the sort of thing you get with .extend()). In that case, you could try registering a custom transformer, something like:
App.ObjectTransform = DS.Transform.extend({
deserialize: function(json){
return Ember.Object.create(json);
}
});
App.User = DS.Model.extend({
profile: DS.attr('object')
});
See: https://github.com/emberjs/data/blob/master/TRANSITION.md#json-transforms
If you are doing your server requests without an adapter you can use the model class method load() with either an array of json objects or a single object. This will refresh any known records already cached and stash away the JSON for future primary key based lookups. You can also call load() on a model instance with a JSON hash as well but it will only update that single model instance.
Its unclear why you are not using an adapter, you can extend one of the Ember Model adapters and override the the record loading there, eg. extend from the RESTAdapter and do any required transform on the JSON if required by overriding _loadRecordFromData
You can also override your models load function to transform data received if required as well. The Ember Model source is fairly easy to read so its not hard to extend to your requirements.