I'm trying to use my AutoFilterer library to filtering entities. I've complex types in such as RangeMin and Max properties in it.
ABP Framework version: 3.3.1
User Interface: MVC
public class BookFilterDto : PaginationFilterBase
{
[StringFilterOptions(StringFilterOption.Contains)]
public string Title { get; set; }
[OperatorComparison(OperatorType.Equal)]
public string Language { get; set; }
[StringFilterOptions(StringFilterOption.Contains)]
public string Author { get; set; }
public Range<int> TotalPage { get; set; }
public Range<int> Year { get; set; }
public BookType[] BookType { get; set; }
}

Also swagger able to send request like domain.com/books?totalPage.min=250.
I've tried a couple of way to run my query:
bookstore.books.book.getList({'TotalPage.min':3})
bookstore.books.book.getList({totalPage: { min: 3}})
None of them didn't work.

I created an example application service:
````csharp
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace MyCompanyName.MyProjectName
{
public class BookAppService : ApplicationService
{
public async Task
{
return input.Name + " / " + input.Price?.Min + "-" + input.Price?.Max;
}
}
public class TestDto
{
public string Name { get; set; }
public Range<int> Price { get; set; }
}
public class Range<T>
{
public T Min { get; set; }
public T Max { get; set; }
}
}
````
And requested via JS proxies like that:
js
myCompanyName.myProjectName.book.getTest({
name: 'my book',
price: { Min: 5, Max: 42 }
}, {
dataType: 'text'
}).then(function(result) {
console.log(result);
});
It works and the result is my book / 5-42 as expected. The request URL: https://localhost:44303/api/app/book/test?name=my%20book&price.Min=5&price.Max=42
The problem here is that I had to pass Min and Max instead of min and max, because the generated proxy was:
js
myCompanyName.myProjectName.book.getTest = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/app/book/test' + abp.utils.buildQueryString([{ name: 'name', value: input.name }, { name: 'price.Min', value: input.price.Min }, { name: 'price.Max', value: input.price.Max }]) + '',
type: 'GET'
}, ajaxParams));
};
I think we only need to solve the PascalCase - camelCase problem while creating the Min & Max naming in the service proxies. It should be:
js
myCompanyName.myProjectName.book.getTest = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/app/book/test' + abp.utils.buildQueryString([{ name: 'name', value: input.name }, { name: 'price.min', value: input.price.min }, { name: 'price.max', value: input.price.max }]) + '',
type: 'GET'
}, ajaxParams));
};
I haven't deeply checked the #6290 yet.
It tries to build with each parameter.
For example, even I don't set Price property, but javascript uses it as input.price.min, so price is undefined at that situation.
But if it use as input['price.min'] everything'll work great 馃憣 The PR #6290 solves that situation
I prefer
js
myCompanyName.myProjectName.book.getTest({
name: 'my book',
price: { min: 5, max: 42 }
})
instead of
js
myCompanyName.myProjectName.book.getTest({
name: 'my book',
'price.min': 5,
'price.max': 42
})
because the first one better matches to the method and DTO structure defined in the C# side. Proxy script should handle the conversion inside it by tolerating the null case.
As a pattern, that is much more useful, Maybe a null-check might be better solution on javascript side.
When price parameter is optional, following line has error, because of price is null/undefined, and javascript trying to access min or max property of price.
url: abp.appPath + 'api/app/book/test' + abp.utils.buildQueryString([{ name: 'name', value: input.name }, { name: 'price.min', value: input.price.min }, { name: 'price.max', value: input.price.max }]) + '',
Same example without sending price:
myCompanyName.myProjectName.book.getTest({
name: 'my book'
})
It throws Uncaught TypeError: Cannot read property 'min' of undefined
I really don't know best practices at javascript but, as I see ? operator is supported 82% of browsers. (here)
May be generated code can has null check like input.price?.min or with any other situations: input.propA?.propB?.propC
In that situation, following generated script might work 82% of browsers according to caniuse Optional chaining operator
myCompanyName.myProjectName.book.getTest = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/app/book/test' + abp.utils.buildQueryString([{ name: 'name', value: input.name }, { name: 'price.min', value: input.price?.min }, { name: 'price.max', value: input.price?.max }]) + '',
type: 'GET'
}, ajaxParams));
};
Both of usage is working now. I can use input['name'] = "My Book" and input.name = "My Book".
Javascript allows to set properties with both way. Reading is same, too;
console.log(input["name"]) and console.log(input.name) do the same thing.I've just realized. dot notation doesn't work for nested objects. And this PR breaks structure of DTO as you say @hikalkan
But solves another issues like handling custom ParameterName if it's set in application.
var book1 = { name: "My book" }
console.log(book1.name); // Works great!
console.log(book1["name"]); // Works great!
var book2 = { price: { min: 5, max:42} };
console.log(book2.price.min); // Works fine!
console.log(book2["price.min"]); // Doesn't work!
console.log(book2["price"]["min"]); // Works fine. But throws error if price is undefined,
What do you think @maliming ?
hi @enisn
My thoughts is:
book.price is an object. but for query string and form-data, they should be key-value.
book.price.min is the key.
var formdata = new FormData();
formdata.append("book.name", "my book");
formdata.append("book.price.max", "10");
formdata.append("book.price.min", "6");
Although the use of objects is friendly to c#, in fact, it is key-values. : )