Locode user reset AutoFilter to no filter

I’ve got something like this:

    [Route("/departments", "GET")]
    [Route("/departments/{DepNo}", "GET")]
    [ValidateRequest("IsAuthenticated")]
        [AutoFilter(Field ="Active", Value =true)]
    public class QueryDepartments
        : QueryDb<Department>, IReturn<QueryResponse<Department>>, IGet
    {
        public int? DepNo { get; set; }

        public bool? Active { get; set; }
    }

The Active flag is a boolean in the database, similar to a DeletedDate.

A user would usually only be interested in active departments, so therefore I used the AutoFilter attribute. On certain occations the user wants to see everything. How can the user, with Locode, reset the AutoFilter? I don’t want to click the Active column to set true or false. I want to clear it, so it becomes NULL, and thus returs both.

Any idea how I can do this? Shouldn’t the Locode UI show that there’s a default filter applied? Perhaps the AutoFilter should have an attribute allowUserOverride or something…

AutoFilter attributes are not the same as filters in Locode. They are a way to enforce values in the DB query when the API is used. Filter values that differ on the Active value will be ignored either way. This is useful when enforcing things like multi-tenancy and other use cases where you don’t want users to ever override, but not for default filtering options that the user can override as you’ve found.

Locode filtering is stored in the user’s browser via LocalStorage, so if it was ‘cleared’ it would stay like that until it was added back via the filtering UI.

Looking into the issue of having it overridable in API/filters, can you confirm with database you are using with AutoQuery/Locode? Any other details about your setup will also help, eg ServiceStack version, .NET version etc. Thanks.

Thanks for super quick reply as always, never ceases to amaze me!
These are not bugs, but ways to get more usage out of ServiceStack’s Locode without creating custom components.

I’ve removed the AutoFilter, so now the DTO is like this:

    [Route("/departments", "GET")]
    [Route("/departments/{DepNo}", "GET")]
    [ValidateRequest("IsAuthenticated")]
    public class QueryDepartments
        : QueryDb<Department>, IReturn<QueryResponse<Department>>, IGet
    {
        public int? DepNo { get; set; }
        public bool? Active { get; set; }
    }

ServiceStack version is the latest (just did x new web-tailwind yesterday) and I’m on .NET 7.
DTO’s were generated using AutoQuery “database first”, then made the DTOs using the x dtos command (now it seems to be called x mjs).

I can filter on both Active = true and Active = false, because it’s nullable. In Locode, it shows both Active/Inactive. If I apply filter I can get either one. So it all works like I expect.

What I’d like to achieve is a way to set default filters for a user, using Locode for the first time (so there’s no filters in the user’s LocalStorage). Just like customizing Locode w. input types, formatters, I’d like to customize with good default values.

A good default value is Active = true.

Is there any attribute that I could use for this? Just like the format and input-type attributes…

There’s also an issue with API Explorer. Suggest a better input control for nullable boolean values (a way to separate null from false. Example:

The UI is only going to have dual states for booleans, either “checked/true” or not.

There’s no ability to set default filters, if you’re showing Active column in your returned dataset you should be able to query it with a URL:

example.org/ui/QueryDepartments?Active=true

That you could provide links to, e.g. in a custom HTML page in your App.

If they want it permanent then they’ll need to add a filter. You could probably populate the filter yourself with some javascript in custom.js that checks for the existence of a filter, if not populates it, e.g like:

localStorage.setItem("Column/AutoQueryGrid:Department.Active", 
    `{"filters":[{"key":"%","name":"=","value":"true"}]}`)

Not tested this myself, but basically you’d want to create all the default filters for all the APIs you want, then look at what it generates in localStorage that you would copy and populate by default when they don’t exist.

1 Like

Yes, regarding Locode, that works. I’ve now made this in wwwroot\modules\locode\custom.js

if (!localStorage.hasOwnProperty("locodeDefaultsInitialized")) {
    localStorage.setItem("Column/AutoQueryGrid:Department.Active",
        `{"default":true,"filters":[{"key":"%","name":"=","value":"true"}]}`)
    localStorage.setItem("locodeDefaultsInitialized",true)
}

The first time a user visits the page, or if LocalStorage was cleared, default filter will apply. Not as easy/elegant as a property, but not bad.

Regarding the UI for the boolean, I agree checkbox for booleans is good. However:

public bool? Active { get; set; }

This is not a boolean, but a nullable boolean. I argue that there’s a difference: A nullable boolean has three values, null being the third.

The API Explorer only allows me to select two of them, and which two depend on if it’s my first page visit or not.

As the screenshots show: on the first visit, if I don’t check the Active box, it’s sent as a null (or not sent). After I check then uncheck, it’s sent as false. So it’s both inconsistent and not functional (can’t purposely clear it). I suggest nullable booleans be displayed as three radio buttons instead as default.

I’m aware there’s a difference, but there’s no tri-state boolean checkbox so the UI can only represent true or non-true states, which if you’re relying on UIs with checkboxes your DataModels should only have non-nullable booleans so it can only capture the 2 boolean states.

If you want tri-states use an enum with descriptive names for what each state means so you get a dropdown instead.

That’s why I suggest radio buttons. Don’t see any downside to that, but of couse I don’t know all the internals.

The drop-down works of course:

    public class QueryDepartments
        : QueryDb<Department>, IReturn<QueryResponse<Department>>, IGet
    {
        public int? DepNo { get; set; }
//        public bool? Active { get; set; }
        public enTrueFalseNotSet ActiveFilter { get; set; }   
    }

Then override the implementation:



    public QueryResponse<Department> Any(QueryDepartments request)
    {
        using var db = AutoQuery.GetDb(request, base.Request);
        var q = AutoQuery.CreateQuery(request, base.Request);

        switch (request.ActiveFilter) {
            case enTrueFalseNotSet.True:
                q = q.Ensure(x => x.Active);
                break;
            case enTrueFalseNotSet.False:
                q = q.Ensure(x => !x.Active);
                break;
        }

        var results = AutoQuery.Execute(request, q);
        return results;
    }

Now I’m able to differentiate in the API Explorer UI. A bit of extra work compared to out-of-the-box though, and it now differs more from actual use (in a query I’d still pass ?active=true or not pass active at all).

It doesn’t stop me in my work, just a suggestion for an improvement I guess.