Warm tip: This article is reproduced from serverfault.com, please click

How to set default option for dynamic select control in Node-Express-Mongoose?

发布于 2020-11-28 03:48:50

I'm now working in Node-Express environment with PUG as a view engine.

I have CompanySchema with one of the fields "comp_ParentName" which referring to schema itself:

comp_ParentName: {type: Schema.Types.ObjectId, ref: 'Company', required: false},

It's not required because not every company has a parent one. And because of this I want to give users an option to leave this filed empty when they they want to create a new company (in those cases when company doesn't have a parent company or when a user simply don't know if the company has any parent company at all).

I have some test code in my "Create New Company" form but unfortunately it doesn't serve 100% to my purpose:

div.form-group.row
  label(class="col-sm-2 col-form-label col-form-label-sm")(for='comp_ParentName') Parent company:
  div.col-sm-10
    select#comp_ParentName.form-control(class="form-control form-control-sm" type='select', placeholder='Select parent company' name='comp_ParentName' required='false' )
      - companies.sort(function(a, b) {let textA = a.comp_OfficialName_ENG.toUpperCase(); let textB = b.comp_OfficialName_ENG.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;});
      for company in companies
        if company
          option(value=company._id selected=(company._id.toString()===company._id.toString() ? 'selected' : false) ) #{company.comp_OfficialName_ENG}
        else
          option(value=company._id) #{company.comp_OfficialName_ENG}

The problem that I face with the above code is that I always have one "preselected" company in the form but as I said I want to have an option to leave this field empty. I tried to add some default option like "Choose Parent Company" with the help of

option(value="" disabled selected) Choose Parent Company

but every time when I try to save a new company the "Please Fill Out this Field" tooltip pops-up.

Is there any possible workaround?

Here is my code that handles Company create in my companyController.js:

exports.company_create_post = [
// Convert the companyrole to an array.
(req, res, next) => {
    if(!(req.body.companyrole instanceof Array)){
        if(typeof req.body.companyrole ==='undefined')
        req.body.companyrole = [];
        else
        req.body.companyrole = new Array(req.body.companyrole);
    }
    next();
},

// Validate and sanitise fields.
body('comp_OfficialName_ENG', 'Must not be empty.').trim().isLength({ min: 1 }).escape(),
body('comp_ShortName_ENG', 'Must not be empty.').trim().isLength({ min: 1 }).escape(),
body('comp_GroupName_ENG', 'Must not be empty.').trim().isLength({ min: 1 }).escape(),
body('comp_Role.*').escape(),
body('comp_cntrID', 'comp_cntrID must be specified').trim().isLength({ min: 1 }).escape(), //reference to the associated country
body('comp_stID', 'comp_stID must be specified').trim().isLength({ min: 1 }).escape(), //reference to the associated state
body('comp_ctyID', 'comp_ctyID must be specified').trim().isLength({ min: 1 }).escape(), //reference to the associated city

// Process request after validation and sanitization.
(req, res, next) => {
    
    // Extract the validation errors from a request.
    const errors = validationResult(req);

    // Create a Company object with escaped and trimmed data.
    var company = new Company(
      { comp_OfficialName_ENG: req.body.comp_OfficialName_ENG,
        comp_ShortName_ENG: req.body.comp_ShortName_ENG,
        comp_GroupName_ENG: req.body.comp_GroupName_ENG,
        comp_ParentName: req.body.comp_ParentName,
        comp_Role: req.body.companyrole,
        comp_cntrID: req.body.comp_cntrID,
        comp_stID: req.body.comp_stID,
        comp_ctyID: req.body.comp_ctyID
       });

    if (!errors.isEmpty()) {
        // There are errors. Render form again with sanitized values/error messages.

        // Get all Company roles for form.
        async.parallel({
            companyroles: function(callback) {
                CompanyRole.find(callback);
            },
        }, function(err, results) {
            if (err) { return next(err); }

            // Mark our selected companyroles as checked.
            for (let i = 0; i < results.companyroles.length; i++) {
                if (company.companyrole.indexOf(results.companyroles[i]._id) > -1) {
                    results.companyroles[i].checked='true';
                }
            }
            res.render('company_form', { title: 'Create Company', companyroles:results.companyroles, company: company, errors: errors.array() });
        });
        return;
    }
    else {
        // Data from form is valid. Save company.
        company.save(function (err) {
            if (err) { return next(err); }
               //successful - redirect to new company record.
               res.redirect(company.url);
            });
    }
}];

UPDATE: With the help of @Ankur R the problem was mostly resolved but now I'm facing another issue. It's absolutely fine when company has a parent one and I can choose the name of the parent company in the dropdown list of my select control. BUT, in those cases when company doesn't have a parent company the dot notation in my company_detail.pug stop working and gives me an error.

company.comp_ParentName.comp_OfficialName_ENG

it says "Cannot read property 'comp_OfficialName_ENG' of undefined". Which is quite logical bearing in mind that a zero value was assigned to it:

option(value="" selected) No Parent Company

Here is my select control in "company_form.pug" (just to show how "comp_ParentName" is created) :

div.form-group.row
  label(class="col-sm-2 col-form-label col-form-label-sm")(for='comp_ParentName') Parent company:
  div.col-sm-10
    select#comp_ParentName.form-control(class="form-control form-control-sm" type='select', name='comp_ParentName' )
      - companies.sort(function(a, b) {let textA = a.comp_OfficialName_ENG.toUpperCase(); let textB = b.comp_OfficialName_ENG.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;});
      option(value="" selected) No Parent Company
      for company in companies
        if company
          option(value=company._id selected=(company._id.toString()===company._id.toString() ? 'selected' : false) ) #{company.comp_OfficialName_ENG}
        else
          option(value=company._id) #{company.comp_OfficialName_ENG}

and this is the code in "company_detail.pug" where dot notation stop working saying that "comp_ParentName" is undefined.

div.form-group.row
  label(class="col-sm-2 col-form-label col-form-label-sm")(for='comp_ParentName') Parent company:
  div.col-sm-3
    input#comp_ParentName.form-control(class="form-control rdonly form-control-xs" type='text', name='comp_ParentName' value=(undefined===company ? '' : company.comp_ParentName.comp_OfficialName_ENG ) readonly)

Any ideas on how to handle this issue?

Questioner
Maxmit
Viewed
0
Ankur R 2020-11-30 12:10:00

Here i have corrected the Company creation code from your controller

// Create a Company object with escaped and trimmed data.
var company = new Company(
  { comp_OfficialName_ENG: req.body.comp_OfficialName_ENG,
    comp_ShortName_ENG: req.body.comp_ShortName_ENG,
    comp_GroupName_ENG: req.body.comp_GroupName_ENG,
    ...(!!req.body.comp_ParentName && { comp_ParentName: req.body.comp_ParentName }),
    comp_Role: req.body.companyrole,
    comp_cntrID: req.body.comp_cntrID,
    comp_stID: req.body.comp_stID,
    comp_ctyID: req.body.comp_ctyID
   });

Also if you want to do more strict checking for an mongo id only then check here.

For ref. using spread operator in object creation follow https://medium.com/@slamflipstrom/conditional-object-properties-using-spread-in-javascript-714e0a12f496