0

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?

Maxmit
  • 13
  • 5

3 Answers3

0

There is an issue with your code of creating select and option both.

I tried with below code and it worked.

form
    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", name="comp_ParentName")
                option(value="" selected) Choose Parent Company
                option(value="1") One
                option(value="2") Two
    div.form-group.row
        button(type="submit") Submit

Updates:

  1. Here in select no need to specify select="true/false" because browser only checks for required attribute.
  2. It's not recommended to disabled to empty value option in select, because once user select other option and want to unselect then it won't be possible.
Ankur
  • 496
  • 1
  • 6
  • 11
  • Thanks @Ankur R ! I'm not sure if it will work for me. First, I don't have a limited number of options in my list, like Option1, Option2, Option3 etc... It actually contains the list of all _IDs of all the companies in "Company" collection. So, I definitely need to use loop. – Maxmit Nov 28 '20 at 05:01
  • Yes you can use the loop. I didn't asked for that change. You just remove require="false" from select. – Ankur Nov 28 '20 at 06:02
  • Also type and placeholder are unsupported attributes for select element in html. If this question has resolved your issue then please accept it as correct answer. – Ankur Nov 28 '20 at 06:04
  • Thanks @Ankur R. I will check later and respond. – Maxmit Nov 28 '20 at 08:21
  • Actually, I quickly checked and it's still not working. If I take your advise and remove require="false" from select - it gives me another error: "Company validation failed: comp_ParentName: Cast to ObjectId failed for value "" at path "comp_ParentName"". If I leave it as is or change it to require="true" - the same tooltip "Please Fill Out this Field" pops-up when I press submit. – Maxmit Nov 28 '20 at 08:49
  • After removing required attribute now it will be posting data along with empty "comp_ParentName" data. So before saving that object directly to db you will have to remove it from the object. For ex. `if(!postData.comp_ParentName) { delete postData.comp_ParentName; }` Here i assume in postData variable your submitted form data is coming. – Ankur Nov 28 '20 at 09:32
  • Thank you @Ankur R. I don't have postData variable in my companyContoleer.js which handles all CRUD operations. Could you please help me a little bit more by advising how and were to include this var and corresponding 'if' statement before the company is saved into db? I will post my code which handles Company create on POST in update. – Maxmit Nov 29 '20 at 03:04
0

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

Ankur
  • 496
  • 1
  • 6
  • 11
0

Finally resolved my problem. It's just I'm quite new to PUG and didn't know how to handle if statement in it. All I had to do was to check if my "company.comp_ParentName" was defined or not. So adding the following if statement helped to resolve the issue:

if (typeof(company.comp_ParentName) == 'undefined')
Maxmit
  • 13
  • 5