select not working in standalone

select not working in standalone

montoyammontoyam Posts: 568Questions: 136Answers: 5

I am trying to create a standalone editor Registration form. I already have an "admin" page for users to go in and approve once somebody submits a registration record. The datatable/editor works fine on the admin page. However, on the registration page (both pages using the same API) , the UserDepartment select list is not working correctly.

admin page (working fine)

        var UsersEditor = new $.fn.dataTable.Editor({
            ajax: 'api/Users',
            table: '#Users',
            fields: [
                { label: "Windows Login:", name: "Users.WindowsLogin" },
                { label: "First Name:", name: "Users.UserFirstName" },
                { label: "Last Name:", name: "Users.UserLastName" },
                { label: "Email Address:", name: "Users.UserEmail" },
                { label: "Department:", name: "Users.UserDepartmentID", type: "select" },
                { label: "Admin?", name: "Users.IsAdmin", type: "select" },
                { label: "Approved?", name: "Users.IsApproved", type: "select" },
                {
                    label: "Record Added",
                    name: "Users.RecordAdded",
                    type: "readonly",
                    def: function () {
                        var d = new Date();
                        return d;
                    }
                }
            ]
        });

        var table = $('#Users').DataTable({
            dom: "Bfrtip",
            ajax: 'api/Users',
            columns: [
                { data: "Users.WindowsLogin" },
                { data: "Users.UserFirstName" },
                { data: "Users.UserLastName" },
                { data: "Users.UserEmail" },
                { data: "Departments.DepartmentName" },
                {
                    data: "Users.IsAdmin",
                    render: function (data, type, row) {
                        return (data == 1) ? "Yes" : "No";
                    }
                },

                {
                    data: "Users.Approved",
                    render: function (data, type, row) {
                        return (data == 1) ? "Yes" : "No";
                    }
                },
                { data: "Users.RecordAdded" },
            ],
            select: true,
            lengthChange: false,
            buttons: [
                { extend: "create", editor: UsersEditor },
                { extend: "edit", editor: UsersEditor },
                { extend: "remove", editor: UsersEditor }
            ]
        });

Registration Page. the dropdown does not populate with the list of departments

    $(document).ready(function () {
        userNameCookie = readCookie('userNameCookie');
        var RegistrationEditor = new $.fn.dataTable.Editor({
            ajax: '/api/Users',
            fields: [
                { label: "Email Address:", name: "Users.WindowsLogin", def: userNameCookie, type:"readonly"},
                { label: "First Name", name: "Users.UserFirstName" },
                { label: "Last Name", name: "Users.UserLastName" },
                { label: "Email Address:", name: "Users.UserEmail" },
                { label: "Department:", name: "Users.UserDepartmentID", type:"select" },
                { label: "Last Name", name: "Users.IsAdmin", type: "hidden" },
                { label: "Last Name", name: "Users.IsApproved", type: "hidden" },
                { label: "Last Name", name: "Users.RecordAdded", type: "hidden" }
            ]
        });

        RegistrationEditor
            .create()
            .title('Add new record')
            .display(true)
            .set("Users.IsAdmin", 0)
            .set("Users.IsApproved", 0)
            .set("Users.RecordAdded", '7/1/2020')
            .buttons('Save')
            ;
    });

controller:

    public class UsersController : ApiController
    {
        [Route("api/Users")]
        [HttpGet]
        [HttpPost]
        public IHttpActionResult Users()
        {
            var request = HttpContext.Current.Request;
            var settings = Properties.Settings.Default;

            using (var db = new Database(settings.DbType, settings.DbConnection))
            {

                var response = new Editor(db, "Users", "WindowsLogin")
                    .Model<UsersModel>("Users")
                    .Field(new Field("Users.RecordAdded")
                        .Set(false)
                    )
                    .Field(new Field("Users.UserDepartmentID")
                        .Validator(Validation.NotEmpty())
                        .Validator(Validation.Numeric())
                        .Options(new Options()
                                    .Table("Departments")
                                    .Value("DepartmentID")
                                    .Label("DepartmentName")
                        )
                    )
                    .Field(new Field("Departments.DepartmentName"))
                    .Field(new Field("Users.IsApproved")
                        .Validator(Validation.NotEmpty())
                        .Validator(Validation.Numeric())
                        .Options(() => new List<Dictionary<string, object>>{
                            new Dictionary<string, object>{ {"value", "0"}, {"label", "No"} },
                            new Dictionary<string, object>{ {"value", "1"}, {"label", "Yes"} },
                        })
                    )
                    .Field(new Field("Users.IsAdmin")
                        .Validator(Validation.NotEmpty())
                        .Validator(Validation.Numeric())
                        .Options(() => new List<Dictionary<string, object>>{
                            new Dictionary<string, object>{ {"value", "0"}, {"label", "No"} },
                            new Dictionary<string, object>{ {"value", "1"}, {"label", "Yes"} },
                        })
                    )
                    .LeftJoin("Departments", "Departments.DepartmentID", "=", "Users.UserDepartmentID")
                    .Process(request)
                    .Data();
                return Json(response);
            }
        }
    }

API\Users returns Department options correct, again, the Admin page works just fine. And if I change the registration form, taking away type: "select" I am able to type in a DepartmentID and the record will save just fine.

This question has an accepted answers - jump to answer

Answers

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406
    edited April 2020

    I can't see an obvious reason for this. I have dozens of situations like this and it always works.

    Normally I would say: "You need an event to make sure initialization of the data table has been completed before you start doing what you want to do." But there is no data table here ...

    Maybe RegistrationEditor is opened before all data have been loaded?! Hence the options - even though API\Users returns them as you say - are not available in the select drop down because Editor opened BEFORE the options were available.

    What you can try: Just use a simple setTimeout for RegistrationEditor to find out. If this resolves the issue you can still think about a more "elegant" solution using a promise for example.

    setTimeout(function() {
        RegistrationEditor
            .create()
            .title('Add new record')
            .display(true)
            .set("Users.IsAdmin", 0)
            .set("Users.IsApproved", 0)
            .set("Users.RecordAdded", '7/1/2020')
            .buttons('Save')
            ;                    
    }, 3000);
    

    Edit: I use this on one occasion. Could be the solution here, too ...
    Only open your editor AFTER all ajax operations have been completed. I would prefer to use a data tables event handler for this as well: but there seems to be none for this case without having a data table ...

    $(document).ajaxStop(function () {
        RegistrationEditor
            .create()
            .title('Add new record')
            .display(true)
            .set("Users.IsAdmin", 0)
            .set("Users.IsApproved", 0)
            .set("Users.RecordAdded", '7/1/2020')
            .buttons('Save')
            ;  
     });                  
    

    @colin: can you help with this?

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    that makes sense. Unfornatunately what you suggested didn't work.

        $(document).ready(function () {
            userNameCookie = readCookie('userNameCookie');
            var RegistrationEditor = new $.fn.dataTable.Editor({
                ajax: 'api/Users',
                fields: [
                    { label: "Email Address:", name: "Users.WindowsLogin", def: userNameCookie, type:"readonly"},
                    { label: "First Name", name: "Users.UserFirstName" },
                    { label: "Last Name", name: "Users.UserLastName" },
                    { label: "Email Address:", name: "Users.UserEmail" },
                    { label: "Department:", name: "Users.UserDepartmentID", type: "select"},
                    { label: "IsAdmin", name: "Users.IsAdmin", type: "hidden" },
                    { label: "IsApproved", name: "Users.IsApproved", type: "hidden" },
                    { label: "RecordAdded", name: "Users.RecordAdded", type: "hidden" }
                ]
            });
    
            $(document).ajaxStop(function () {
                alert("got here");
                var todayDate = new Date();
                RegistrationEditor
                    .create()
                    .title('Add new record')
                    .display(true)
                    .set("Users.IsAdmin", 0)
                    .set("Users.IsApproved", 0)
                    .set("Users.RecordAdded", todayDate)
                    .buttons('Save')
                    ;
            });   
    

    I do get the 'got here' alert though.

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406

    ... Too bad ... I never got “stand alone“ working properly. I always emulate it by using Editor with a hidden data table that has just one column ... Not “elegant“ but at one point you just need a solution ... desperately.

    Advantage: All data tables event handlers work and you can free your mind from the concept of this being a “special“ situation - because it isn't!

  • allanallan Posts: 61,667Questions: 1Answers: 10,096 Site admin

    the UserDepartment select list is not working correctly.

    As in it won't populate? No - it won't without a DataTable as Editor listens for the xhr event to see if the data DataTables has loaded contains any options for its lists. Since you don't have a DataTable for this form, that can't option.

    There are two options here:

    1. Use the options parameter for the select field to list the options (which you'll need to have got somehow).
    2. Or use field().update() to populate the list of options for the select - which again you'll need to get from the database (likely using Ajax if you use this method).

    Personally for a login form I would suggest option 1 to make it as fast as possible (i.e. embed the options in the page source dynamically).

    I should also point out that the .NET libraries for Editor are not intended for use with standalone editing - they make a number of assuptions to be a row based editor - e.g. everything submitted will have an id (or in the case of create, have an id returned).

    Allan

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406

    No - it won't without a DataTable as Editor listens for the xhr event to see if the data DataTables has loaded contains any options for its lists. Since you don't have a DataTable for this form, that can't option.

    That explains why I never got along with Editor "stand alone"! So my "trick" with the hidden one column "pseudo"-Data Table seems to have been the right one.

    @allan, can you update the docs with a big warning on the point you made above please. I wasted so much time figuring this out over a year ago. And only today I understand what the problem was really.

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406
    Answer ✓

    @montoyam
    Just a few weeks ago I built an Editor "stand alone" function to copy user roles from one selected user to a target user. This also requires an options instance server side which - as we learned now - isn't populated in "true" "stand alone" Editor.

    I implemented this as a "fake" "stand alone" using a hidden pseudo data table. It worked straight away.

    This was the HTML for the pseudo data table and the JS for the Editor and the pseudo data table. The rest is just "normal" like with any Editor instance ...

    The Editor instance server side (which I didn't include because it is PHP) looks a bit weird: It only consists of one big options instance retrieving the options for "user.id". The data table itself returns only one row with just one id field in it - which I don't need for anything ... Hence I minimized the amount of data returned from the server: It is just the options plus the (inevitable) "pseudo"-row.

    <!--tblCtrCopyUserRoles: Copy Ctr User Roles from one user to a target user-->
    
    <div class="container hidden">
        <table id="tblCtrCopyUserRoles" class="table table-striped table-bordered"
               cellspacing="0" width="100%">
            <thead>            
                <tr>
                    <th>1</th>
                </tr>
            </thead>
        </table>     
    </div>
    
    var ctrCopyUserRolesEditor = new $.fn.dataTable.Editor( {
        ajax: {
            url: 'actions.php?action=tblCtrCopyUserRoles',
            data: function ( d ) {
                var selected = userTable.row( {selected: true} ); 
                if (selected.any()) {
                    d.user = selected.data().user.id;
                }
            }
        },    
        table: "#tblCtrCopyUserRoles",
        fields: [ {
                label: lang === 'de' ? 'Ziel-Nutzer:' : 'Target-User:',
                name:  "user.id",
                type: "selectize", 
                opts: {
                    create: false,
                    maxItems: 1,
                    openOnFocus: false,
                    allowEmptyOption: false,
                    placeholder: lang === 'de' ? 'Bitte einen Ziel-Nutzer auswählen' :
                                                 'Please select a Target-User'
                }
            }
        ]        
    } );
    
    var ctrCopyUserRolesTable = $('#tblCtrCopyUserRoles').DataTable( {
        ajax: {
            url: 'actions.php?action=tblCtrCopyUserRoles',
            type: 'POST',
            data: function ( d ) {
                var selected = userTable.row( {selected: true} ); 
                if (selected.any()) {
                    d.user = selected.data().user.id;
                }
            }
        },    
        columns: [           
            { data: "user.id" }
        ]
    } );
    
    
  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    yes, I hate to say this but the documentation for standalone is pretty hard to follow. It is unclear how to format the HTML and such :(

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    thanks rf1234...if I continue with the standalone method I may give this a try. But, in your code, what is userTable? Do you have another datatable on the page that is visible? and if ctlCopyUserRoles table is hidden, why do you have a placeholder for target userid? Im not quite following your code.

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406
    edited April 2020

    User Table is the parent table of the "pseudo"-data table that you can't see. Only its Editor is visible on button click.

    It is quite simple:
    - You select a user in the user table (the source user)
    - then you click "Copy User Roles"
    - an Editor popup opens with a dropdown to select the target user - hence the placeholder

    Like this:

    That is the custom button definition:

    //custom button to copy the user roles from user a to user b in contract management
    $.fn.dataTable.ext.buttons.ctrCopyUserRoles = {
        extend: 'edit',
        text: ctrCopyUserRolesLabel,
        name: "ctrCopyUserRolesButton",
        action: function ( e, dt, button, config ) {
            ctrCopyUserRolesEditor
                .title($.fn.dataTable.Editor.defaults.i18n.edit.title)
                .buttons({
                        label: $.fn.dataTable.Editor.defaults.i18n.edit.submit,
                        className: 'btn-showall-color',
                        fn: function () {
                            this.submit();
                        }
                    })
                .edit( ctrCopyUserRolesTable.rows().indexes() );
        }
    };
    

    text: is assigned a variable which is filled with text in the user language.

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    oh, that is cool. thanks

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406

    Thanks. In this case the back end Editor instance (which is PHP here) does absolutely nothing except for retrieving the options. I use a "preEdit" event to make sure it doesn't do any updates because the editing is too special in this case.

    ->on('preEdit', function ( $editor, $id, $values ) {
        if ( isset($values['user']['id']) ) {  
            copyCtrUserRoles( $editor->db(), $_POST['user'], $values['user']['id'] );
        }
        return false;
    } )
    

    Why am I using Editor for this at all? I want it all done in the same way and fully integrated with everything else around the user table.

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    yeah, I would rather use the databases libraries for all database functions, and not have to use razor and controllers to write to the database.

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    oh, i may have not mentioned that I am using asp.net, not php

  • rf1234rf1234 Posts: 2,806Questions: 85Answers: 406

    no problem, I didn't post a lot of PHP because I know that you don't use it ... Many people don't like PHP because you can write very bad code with it ... But you don't have to :smile:

  • montoyammontoyam Posts: 568Questions: 136Answers: 5

    @rf1234 , I added the hidden DataTable and the select box works perfectly. Thank you.

This discussion has been closed.