Hi, guys
I need to show the apps according to the selected OS: Android or IOS.
The OS is chosen by a Radio Button, and after that it would need to filter the dropdown with the respective OS apps. I know this can be done by typescript, which is where I'm trying, but to no avail so far.
The main question, besides how to make this filter, is how it would retrieve the information, relating app and OS, since they are in a SQL table.
Any suggestions are welcome. Thanks.
Hi @leovamos ,
can you show a screenshot / mockup what you are trying to achieve so we can better understand?
Is the dropdown within a Serenity form (an official form field) which has a lookup editor on it?
Tell us more...
With kind regards,
John
Hi @leovamos ,
ah - if the main problem here is how to fetch data on the client-side (the whole xyzDialog.ts is client-side) over from the server side (SQL and xyzEndpoint.cs) - then look at this wiki: https://github.com/volkanceylan/Serenity/wiki/BBB:-On-client-side,-fetching-a-value-from-server-side-out-of-database
Here I explain how to fetch data from server-side to client-side.
But I would be very interested in your RadioButtonEditor code :-) As this is really an interesting feature.
Hope, above helps a bit.
Otherwise, let me know :-)
With kind regards,
John
@leovamos ,
I am unfortunately not good in converting code within my mind into a visualization.
Please use MS paint or so to quickly draw a mockup on what you want to achieve - with comments beside the form fields (what should happen when in the fields a selection is made).
If I understand you correctly, you want to fill the 2nd dropdown field based on a choice in 1st dropdown, correct?
For this the best way is to fill the 1st form field via a lookup editor (your OS choice).
To the 2nd dropdown field, assign the EmptyLookupEditor (from dfaruque's Serenity.Extra - a repository here on Github) so that the 2nd dropdown stays empty.
And then in the onChange-Event of the 1st dropdown do a query via xyzService on the client side (by passing the just selected value from the 1st dropdown) to the server side (via a custom written endpoint (an additional entry in the xyzEndpoint.cs within the entity which will deliver the desired data for the 2nd dropdown) and the matching sql query in xyzRepository.ts.
The Query should only deliver a list of ids with the corresponding texts. This list is returned to the xyzEndpoint and from there over to the client-side.
And then do a foreach on the returned data and add the items to the 2nd dropdown (a jquery select2 widget).
If you want a default selected value in the 2nd field, you have to do a this.form.<2ndField>.value = <some existing id from the returned list>.
Hope this gives you an overview on what to do.
With kind regards,
John
create lookup: AppLookup.cs
```
[LookupScript("Default.AppLookup")]
public class AppLookup: RowLookupScript
{
public AppLookup()
{
IdField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Id.PropertyName;
TextField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Name.PropertyName;
}
protected override void PrepareQuery(SqlQuery query)
{
var fld = DashboardSMICv2.Smic.Entities.AppRow.Fields;
query.Select(fld.Id)
.Select(fld.Name)
.Select(fld.Platform)
.Where(fld.Archive == 0);
}
protected override void ApplyOrder(SqlQuery query)
{
}
}
in AppTrustDialog.ts
this.form.Platform.change((e) => {
this.form.RailcarTypeId.cascadeField = "Platform";
this.form.RailcarTypeId.cascadeValue = this.form.Platform.value;
})
``
Hi @luongnv6
Thanks for the suggestion, but I don't quite understand what this code does. If you can explain the idea to me, I'm grateful.
And we have two problems here: there is no fld.Archive field in AppLookup ... I don't know where that comes from by the way. The other problem is that the form in AppTrustDialog doesn't have RailcarTypeId, which I don't know where it comes from either. If you can better explain these things and work out how to solve them, that's great. I will test the code as soon as there is a way to fix these problems.
Hi @leovamos ,
the problem why all your grid rows are gone is because you have changed the original xyzEndpoint.cs controller type from what it was to "controller".
This does not work :-)
In my example you have to create an own - independent - controller (e.g. create it with sergen) and _then_ change the class of that new controller (while leaving the original one in peace).
But I will try to make up an end to end example how to fill an empty dropdown with a list of values from the server side from within the original controller.
Give me some time...
John
Oh, I understand now. Thanks for explaining.
Ok, @JohnRanger, I'll be waiting for your example, and meanwhile, I'll try to understand how to solve this problem. Thanks again!
Use the EmptyLookupEditor from @dfaruque 's Serenity.Extra (https://github.com/dfaruque/Serenity.Extra)
Decorate the two appsId fields in your xyzRow.cs with it. [EmptyLookupEditor]
Create a new file called Select2DataItem.cs
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DashboardSMICv2.Modules.Common.Helpers
{
public class Select2DataItem
{
public int id { get; set; }
public string text { get; set; }
}
}
Create a new Endpoint file called DataProvider.cs with the following content:
using System;
using DashboardSMICv2.Modules.Common.Helpers;
using System.Web.Mvc;
using System.Linq;
using Serenity.Data;
using System.Data;
using System.Collections.Generic;
using Newtonsoft.Json;
using appRow = DashboardSMICv2.Smic.Entities.AppRow;
namespace DashboardSMICv2.Common.Endpoints
{
[RoutePrefix("Services/Common/DataProvider"), Route("{action}")]
public class DataProviderController : Controller
{
[HttpPost]
public ActionResult AppsForPlatform(int FilterId)
{
var connection = SqlConnections.NewFor<appRow>();
var query = "select id, name from approw where platformId = " + FilterId;
var Result = connection.Query(query);
connection.Close();
List<Select2DataItem> DataItems = new List<Select2DataItem>();
foreach (var Resultrow in Result)
{
var data = (IDictionary<string, object>)Resultrow;
var currentItem = new Select2DataItem();
currentItem.id = (int)data[approw .Fields.id.Name];
currentItem.text = (string)data[approw.Fields.Name.Name];
}
// *** Re-sort items ***
DataItems = DataItems.OrderBy(o => o.text).ToList();
var output = JsonConvert.SerializeObject(DataItems);
return Json(output, JsonRequestBehavior.AllowGet);
}
}
}
Create a new file called JSONDataFetcher.ts and put the following inside:
namespace DashboardSMICv2.JSONDatafetcher {
// *** Fetches JSON Data from Server side ***
export function GetItemsAsJSON(ServiceEndpointURL: string, FilterId: String) {
let myresult = "";
FilterId = (FilterId == "" ? "0" : FilterId);
// *** Debug only ***
//alert(ServiceEndpointURL);
//alert(FilterId);
$.ajax({
type: "post",
dataType: "json",
async: false,
url: ServiceEndpointURL,
data: {
'FilterId': FilterId
}
,
success: function (response) {
if (response instanceof Object) {
var json = response;
}
else {
var json = JSON.parse(response);
}
var output = json;
myresult = output;
}.bind(this),
error: function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
alert("Error! " + err);
}
});
return myresult;
}
}
In the change event of your RadioButton field (I call it here PlatformId), insert the following:
let ServiceEndpoint = "/services/Common/DataProvider/AppsForPlatform";
var data = JSONDatafetcher.GetItemsAsJSON(ServiceEndpoint, this.form.PlatformId.value;
this.form.AppId1.select2({data: data});
this.form.AppId1.select2({data: data});
That should do it. I use this system in my apps to dynamically fill the dropdowns of fields directly from the database. Never having any caching problems - no matter how the data gets into the DB - and fully offloading the whole work to DB and Web server. That's for what they are made, no? :-)
Important: I didn't test the above code but I just copy&pasted it out of one of my projects and have adapted the project name and some fields - as well as adapted the server-side endpoint to possibly match your requirement.
Hope, this helps.
With kind regards,
John
@leovamos ,
in your screenshot - the Radiobutton line - how did you implement this? As a true Serenity editor or just DOM manipulation?
If it is a true radiobutoneditor, would you mind to share your code for this here?
With kind regards,
John
sorry @leovamos
The above code will cascade the data on App 1 according to the Platform (Android, ISO).
You just need to create a lookupeditor to get all the data for the App1 lookup (including the Platform field).
When you change the radio, you will set the cascade value for the App1 lookup to filter data.
same for App2 lookup
create lookup: AppLookup.cs
```
namespace DashboardSMICv2.Smic.CustomLookup
{
[LookupScript("Default.AppLookup")]
public class AppLookup: RowLookupScript
{
public AppLookup()
{
IdField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Id.PropertyName;
TextField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Name.PropertyName;
}
protected override void PrepareQuery(SqlQuery query)
{
var fld = DashboardSMICv2.Smic.Entities.AppRow.Fields;
query.Select(fld.Id)
.Select(fld.Name)
.Select(fld.PlatformId);
}
protected override void ApplyOrder(SqlQuery query)
{
}
}
}
in AppTrustForm.cs
public class AppTrustForm
{
//.....
[RadioButtonEditor]
public Int32 PlatformId { get; set; }
[LookupEditor(typeof(DashboardSMICv2.Smic.CustomLookup.AppLookup))]
public Int32 App1Id { get; set; }
[LookupEditor(typeof(DashboardSMICv2.Smic.CustomLookup.AppLookup))]
public Int32 App2Id { get; set; }
//......
}
in AppTrustDialog.ts
this.form.PlatformId.change((e) => {
this.form.App1Id.cascadeField = "PlatformId";
this.form.App1Id.cascadeValue = this.form.PlatformId.value;
this.form.App2Id.cascadeField = "PlatformId";
this.form.App2Id.cascadeValue = this.form.PlatformId.value;
})
``
You should correct the field names to match your table structure, the above fields are based on my conjecture only
good luck
@leovamos ,
the solution of @luongnv6 looks for me the much more elegant solution than mine. So I recommend the solution of @luongnv6 .
With kind regards,
John
In row.cs
```
public class AppTrustRow{
[DisplayName("Platform "), NotNull, QuickFilter]
public PlatformEnum? Platform
{
get { return (PlatformEnum?)Fields.Platform [this]; }
set { Fields.Platform [this] = (Int16?)value; }
}
[DisplayName("Archive"), NotNull]
[LookupEditor(typeof(CustomLookup.AppLookup))]
public Int32? AppId1
{
get { return Fields.AppId1[this]; }
set { Fields.AppId1[this] = value; }
}
[DisplayName("Archive"), NotNull]
[LookupEditor(typeof(CustomLookup.AppLookup))]
public Int32? AppId2
{
get { return Fields.AppId2[this]; }
set { Fields.AppId2[this] = value; }
}
[DisplayName("Status"), NotNull, QuickFilter]
public AppTrustStatus ? Status
{
get { return (AppTrustStatus ?)Fields.Status[this]; }
set { Fields.Status[this] = (Int16?)value; }
}
...
public class RowFields : RowFieldsBase
{
....
public Int16Field TypeCode;
public Int32Field AppId1;
public Int32Field AppId2;
public Int16Field Status;
....
}
}
``
In form.cs```
[FormScript("Smic.AppTrust")]
[BasedOnRow(typeof(Entities.AppTrustRow), CheckNames = true)]
public class AppTrustForm
{
[RadioButtonEditor]
public Int16 Platform { get; set; }
public Int32 AppId1 { get; set; }
public Int32 AppId2 { get; set; }
[RadioButtonEditor]
public Int16 Status { get; set; }
}
please T4 after rebuild
thanks
@leovamos ,
I don't have time to reverse-engineer @luongnv6 's proposal - but if you follow mine this would work. Look in the section "In the change event of your RadioButton field..." There I call the server-side webservice with giving over the ID of the chosen OS.
So call the webservice with the ID of the os you get from the Radio button editor.
With kind regards,
John
send me dialog.ts file, please
Also the field "platform" must be in the xyzForm.cs. If you don't want it to be shown, tag it with [hidden].
If it _is_ in the xyzForm.cs, then a T4 transform (sometimes 2x) is neccessary to create the fields on the client-side (so that they appear when you do this.form.xxx.
Also if you do have callbacks, make sure that you put .bind(this) at the end of the callback code (after the closing bracket "}" in order to guide the callback to what scope "this" within the callback code belongs to).
Hint: Within callbacks, Visual studio is not capable to recognize the .bind(this) at the end of the bracket and will currently not have intellisense for the outer "this" object.
Also: Why do you select the radiobuttoneditor so complicated?
Why not:
// *** What to do when platform field is clicked by the user ***
this.form.Platform.element.bind('click',
function (e) {
// *** do something when the platform editor is clicked
var radioValue = this.form.Platform.value; // *** If the radiobuttoneditor is properly implemented and sets the "value" attribute of the form field.
/*$.ajax({
url: 'AppTrustEndpoint.cs/get_data',
type: "post",
data: JSON.stringify(radioValue),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function () {
alert("success");
}
});
}.bind(this)
);
Regards,
John
You should write events in the constructor
```
namespace DashboardSMICv2.Smic {
@Serenity.Decorators.registerClass()
export class AppTrustDialog extends Serenity.EntityDialog
protected getFormKey() { return AppTrustForm.formKey; }
protected getIdProperty() { return AppTrustRow.idProperty; }
protected getLocalTextPrefix() { return AppTrustRow.localTextPrefix; }
protected getService() { return AppTrustService.baseUrl; }
protected form = new AppTrustForm(this.idPrefix);
constructor() {
super();
this.form.Platform.change((e) => {
this.form.AppId1.cascadeField = "Platform";
this.form.AppId1.cascadeValue = this.form.Platform.value;
this.form.AppId2.cascadeField = "Platform";
this.form.AppId2.cascadeValue = this.form.Platform.value;
});
/* $(document).ready(function () {
$("input[name = 'Serenity_RadioButtonEditor10']").click(function () {
var radioValue = $("input[name='Serenity_RadioButtonEditor10']:checked").val();
/*$.ajax({
url: 'AppTrustEndpoint.cs/get_data',
type: "post",
data: JSON.stringify(radioValue),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function () {
alert("success");
}
});
});
});*/
}
protected getToolbarButtons(): Serenity.ToolButton[] {
var buttons = super.getToolbarButtons();
return buttons;
}
}
Hey, @luongnv6
I had some problems with posting code in my issues, and now I'm deleting all my codes from my issues. Could you delete or erase all your comments with codes, please? It will help me a lot.
Most helpful comment
sorry @leovamos
The above code will cascade the data on App 1 according to the Platform (Android, ISO).
You just need to create a lookupeditor to get all the data for the App1 lookup (including the Platform field).
When you change the radio, you will set the cascade value for the App1 lookup to filter data.
same for App2 lookup
create lookup: AppLookup.cs
```
namespace DashboardSMICv2.Smic.CustomLookup
{
[LookupScript("Default.AppLookup")]
public class AppLookup: RowLookupScript
{
public AppLookup()
{
IdField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Id.PropertyName;
TextField = DashboardSMICv2.Smic.Entities.AppRow.Fields.Name.PropertyName;
}
}
in AppTrustForm.cs
public class AppTrustForm
{
//.....
[RadioButtonEditor]
public Int32 PlatformId { get; set; }
[LookupEditor(typeof(DashboardSMICv2.Smic.CustomLookup.AppLookup))]
public Int32 App1Id { get; set; }
[LookupEditor(typeof(DashboardSMICv2.Smic.CustomLookup.AppLookup))]
public Int32 App2Id { get; set; }
//......
}
in AppTrustDialog.ts
``
You should correct the field names to match your table structure, the above fields are based on my conjecture only
good luck