troubleshooting Question

How can one button be used for two different functionalities in the same page?

Avatar of Bruce Gust
Bruce GustFlag for United States of America asked on
Node.jsjQueryJavaScript
3 Comments2 Solutions95 ViewsLast Modified:
I've got a form that looks like this:

<form id="user-form" class="needs-validation" method="post" action="/profile/save" novalidate>

...

      <div class="password-wrapper">
        <button type="button" class="btn btn-primary btn-change-password"><i class="fas fa-fw fa-key"></i> Change Password</button>
        <div class="password-template d-none">
			<div class="row">
				<div class="col-lg-12">
				To change your password, begin by entering your current password first...<br><br>
					<div class="md-form">
						<input type="password" id="current-password" name="current_password" class="form-control" value="">
						<label for="current_password"><a id="expose-current" href="#" style="cursor:text;" tabindex="-1">Current Password</a></label>
					</div>
				</div>
			</div>
			<!-- new row -->
			<div class="row">
				<div class="col-lg-6" style="margin-top:-25px;">
					<div class="md-form">              
						<input type="password" id="user-password" name="user[password]" class="form-control" required />
						<label for="user-password"><a id="exposure" href="#" style="cursor:text;" tabindex="-1">Password*</a></label>
					</div>
				</div>
				<div class="col-lg-6" style="margin-top:-25px;">
					<div class="md-form">
						<input type="password" id="user-confirm" class="form-control" required />
						<label for="user-password">Confirm*</label>
					</div>
				</div>
			</div>
			<div class="row">
				<div class="col-lg-12">
					<div style="font-size:9pt; background-color:#3d5fb5; border-radius:10pt; width:90%; margin:auto; height:auto; padding:5px; text-align:center; color:#fff;">Hover over the "Password" and "Current Password" labels to see the actual characters that you have inputted.</div>
				</div>
			</div>
			<div class="row">
				<div class="col-lg-12">
					<div class="form-group">
						<label>&nbsp;</label><br />
						<button type="submit" class="btn btn-primary save"><i class="fas fa-fw fa-key"></i> Change Password</button>
						<button type="button" class="btn btn-danger btn-cancel-password">Cancel</button>
					</div>
				</div>
			</div><!--end of row -->
			<p>Please ensure that your password meets the following requirements:</p>
			<ul class="password-requirements">
			<li data-reg="upper">At least 1 uppercase letter.</li>
			<li data-reg="lower">At least 1 lowercase letter.</li>
			<li data-reg="number">At least 1 number.</li>
			<li data-reg="special">At least 1 of the following special characters: !@#$%^&*</li>
			<li data-reg="total">At least 8 total characters.</li>
			</ul>
		</div><!-- end of password-wrapper -->

...


<button type="submit" id="btn-save" class="btn btn-success"><i class="fas fa-fw fa-check"></i> Save</button>

</form>

It's a form that has a hidden section which you can reveal by clicking on "change password." Here's how it looks:

before you click on "change password"
And then once you click on "change password,"  you get this:

after you click on "change password"
After you've clicked on "change password," that same button can now be used to submit the form content. You can also use the "save" button.

My question is: How can the same "change password" button be used in two different ways?

The answer lies in the following "user-form.js" file.

/*

HOW TO USE:

To Show: $(window).trigger('user-form-show', ['userId']);
To Hide: $(window).trigger('user-form-hide');

On Complete:

$(window).on('user-form-complete', function(e, data){
  console.log('Form Data: ', data);
});

Be aware that any reference to a specific id on this page will most likely have to be identified in the context of "$form." Since it's not a part of the DOM, but it's being generated dynamically, you're going to have to be more specific that you might otherwise be normally. For example, the code that changes the "user-password" field from "password" are triggered on the "user-from.html.twig" file using "<label for="user-password"><a id="exposure" href="#" style="cursor:text;" tabindex="-1">Passwords*</a></label>." For the system to "see" the "exposure" id, I had to write it like "$form.on('mouseover', '#exposure', function(e){." 

*/

$(document).ready(function(){
	
    $("#chk-user-active").on('change', function () {
        $('#user-active').val(this.checked);        
    });
  var $uModal = $('#modal-user-form'),
      $form = $('#user-form'),
      $passwordButton = $form.find('.btn-change-password').clone(),
      $passwordForm = $form.find('.password-template').clone().removeClass('d-none'),
      data = [],
      loadedUserId = '',
      passwordRegex = {
        upper: /[A-Z]{1,}/,
        lower: /[a-z]{1,}/,
        number: /[0-9]{1,}/,
        special: /[\!\@\#\$\%\^\&\*]{1,}/,
        total: /.{8,}/
      };

  $form.find('.field-phone').mask('000-000-0000');

  $form.find('.password-template').remove();

  $(window).on('user-form-show', function(e, userId, opts){
    // prep the modal
    $uModal.find('.loading').show();
    $uModal.find('.main').hide();

    // show the modal loading screen
    $.magnificPopup.open({
      alignTop: true,
      items: {
        src: $uModal, // can be a HTML string, jQuery object, or CSS selector
        type: 'inline'
      }
    });

    // clear the cached data
    data = {};

    // clear the form from any previous data
    clearForm();
           

    // load the data if a valid id is provided
    if (typeof userId != 'undefined' && userId != 'new') {
      loadFormData(userId);
    } else {
      // set the id for a new user
        loadedUserId = 'new';
        // load drag & drop members 
        teamDragula(userId);

        $form.find('#user-role').attr('data-user-id', loadedUserId);
      // force the password fields
      $form.find('.btn-change-password').replaceWith( $passwordForm.clone() );
      $form.find('.btn-cancel-password').closest('.col').remove();

      // show the form
      $uModal.find('.loading').hide();
      $uModal.find('.main').show();
    }
  });

  $(window).on('user-form-hide', function(){
    $.magnificPopup.close();
  });

    $form.on('submit', function (e) {
		console.log("here");
        let teamArray = [];
        $form.find('#drag-team').find('a').each(function () {
            let uid = $(this).attr('data-user-id');
            if (uid) {teamArray.push(uid);}
        });
        teamArray = $.unique(teamArray);
        $form.find('#user-team').val(JSON.stringify(teamArray));
        $form.find('#user-avatar').val() ? $form.find('#user-avatar').val() : $form.find('#user-avatar').val('https://ui-avatars.com/api/?name=' + $form.find('#user-first-name').val() + '%20' + $form.find('#user-last-name').val()+'&amp;background=33b5e5&amp;size=200&amp;color=000000');
    var $f = $(this),
        $btn = $f.find('button[type="submit"]'),
        html = $btn.html(),
        formData = $f.serialize(),
          route = (loadedUserId !== '') ? $form.attr('action') + '/' + loadedUserId : $form.attr('action');
      // include unchecked checkboxes. use filter to only include unchecked boxes.
      $.each($('.permission-section input[type=checkbox]').filter(function (idx) {
          return $(this).prop('checked') === false;
      }),
          function (idx, el) {
              // attach matched element names to the formData with a chosen value.                
              formData += '&' + $(el).attr('name') + '=' + false;
          }
      );
      

    e.preventDefault();
    e.stopPropagation();

    $f.addClass('was-validated');

    if ($f[0].checkValidity() === false) {
      addMsg('Please check the highlighted fields!');
      return false;
    }

    $btn.html('<i class="fas fa-fw fa-sync-alt fa-spin"></i> Saving...');

    // validate the form and save
    $.post(route, formData, function(resp){
      if (resp.error) {
        clearMsg();
        addMsg(resp.msg);
        return false;
      }

      // finish up
      $(window).trigger('user-form-hide');
      $(window).trigger('user-form-complete', [formData]);
    }, 'json').fail(function(){
      clearMsg();
      addMsg('An unexpected error was encountered while trying to process your request. Please try again.');
    }).always(function(){
      $btn.html(html);
    });
  });

  $form.on('click', '.btn-change-password', function(){
    $(this).replaceWith( $passwordForm.clone() );
  });

  $form.on('click', '.btn-cancel-password', function(){
    $(this).closest('.password-template').replaceWith( $passwordButton.clone() );
  });

  $form.on('keyup', '#user-password', function(e){
    for (var k in passwordRegex) {
      if ( !passwordRegex[k].test(this.value) ) {
        $form.find('ul.password-requirements > li[data-reg="'+k+'"]').removeClass('text-success').addClass('text-danger');
      } else {
        $form.find('ul.password-requirements > li[data-reg="'+k+'"]').removeClass('text-danger').addClass('text-success');
      }
    }
  });

  $uModal.on('click', '#user-form-cancel', function(){
    $(window).trigger('user-form-hide');
  });
        
    var reportsTo=dragula({
        revertOnSpill: true
    }).on('drop', function (el, target, source, sibling) {               
        // something goes here
        });
    $form.find('.dragula').each(function (i) {
        reportsTo.containers.push(this);
    });

  function loadFormData(userId) {
    $.get('/users/' + userId, {}, function(resp){
      if (resp.error) {
        $(window).trigger('user-form-hide');
        return CAMS.alert('Error', resp.msg);
      }

      // deconstruct from ease of use
      var u = resp.data,
          $sRow, html, file;

      // set the id for saving
      loadedUserId = userId;

      // load the form
      $form.find('#user-first-name').val( u.firstName );
      $form.find('#user-last-name').val( u.lastName );
      $form.find('#user-email').val( u.email );
      $form.find('#user-mobile').val( (typeof u.mobile != 'undefined') ? u.mobile : '' );
        $form.find('#user-settings-timezone').val(u.settings.timezone);
        let avtrSrc = '/assets/images/upload-avatar.png';  
        if (typeof u.avatar != 'undefined') {
            if (u.avatar.length > 0) {
                $form.find('.avatar-picker').find('img').attr('src', u.avatar);  
            }
        }
        $form.find('#user-avatar').val(u.avatar);
              
        $form.find('#user-signature').val(u.signature);
        if (typeof u.signature != 'undefined') {
            $form.find('.signature-link').html('<img src="' + u.signature + '"/>');
        } else {
            $form.find('.signature-link').html('<span>______________</span>');
        }
        $form.find('#user-active').val(u.active);
        if (u.active == true) {
            $form.find('#chk-user-active').prop('checked',true);
        } else {
            $form.find('#chk-user-active').prop('checked', false);
        }
        if (typeof u.role != 'undefined') {
            rolePermissions(u.role, u.permissions);
            $form.find('#user-role').attr('data-user-id', userId);
            $form.find('#user-role').val(u.role);
            teamDragula(userId, u.role);
        }
        if (typeof $form.find('#user-account') != 'undefined') {
            $form.find('#user-account').val(u.account);
        }

      // show the form
      $uModal.find('.loading').hide();
      $uModal.find('.main').show();
    }, 'json').fail(function(){
      $(window).trigger('user-form-hide');
      addMsg('An unexpected error was encountered while trying to load your user. Please refresh the page to try again.');
    });
  }

  function clearForm() {
    clearMsg();
    $form.removeClass('was-validated');
    $form.find('input[type!="hidden"]').val('');
      $form.find('.password-wrapper').empty().append($passwordButton.clone());
      $form.find('.avatar-picker').find('img').attr('src', '/assets/images/upload-avatar.png');   
      $form.find('.signature-link').html('<span>______________</span>');
      $form.find('#user-avatar').val('');//hidden field
      $form.find('#user-signature').val('');//hidden field
      $form.find('#user-active').prop('checked', false);
      $form.find('#user-role').val('Sales Representative');
      rolePermissions('Sales Representative');
  }

  function addMsg(txt, type) {
    type = (typeof type == 'undefined') ? 'danger' : type;

    $form.find('.user-messages').append('<div class="alert alert-'+type+'">'+txt+'</div>');
  }

  function clearMsg() {
    $form.find('.user-messages').empty();
  }

    $('#modal-user-form').find('#user-role').on('change', function () {
        teamDragula($(this).attr('data-user-id'),$(this).val());
        rolePermissions($(this).val());
    });
    function teamDragula(userId, role = 'Sales Representative') {
        let teamTitle = '', availableTitle = '', roleMember = '',
            $dragDropSection = $form.find('.team-dragula');
        $dragDropSection.html('Please wait...');
        if (role == 'Sales Manager') {
            teamTitle='Team';
            availableTitle = 'Members';
            roleMember = 'Sales Representative';
        } else {
            teamTitle='Reports to';
            availableTitle = 'Managers';
            roleMember = 'Sales Manager';
        }
        $dragDropSection.html('<div class="col-6 col-small-12  animated zoomIn">'+
                '<h3 class="team-title">'+teamTitle+'</h3><hr />'+
                '<div class="dragula" id="drag-team" style="height: 100px; border: 2px dashed #eef0f3; overflow: auto; min-height: 100px; cursor:grab;"></div>'+                
            '<div class="text-muted"><small>Drag available users avatar and Drop into above to <span class="text-success">ADD</span>. Drag avatar & Drop on available members to <span class="text-danger">REMOVE</span>.</small></div>'+
            '</div>' +
            '<div class="col-2 col-small-12"></div>' +
            '<div class="col-4 col-small-12 animated zoomIn">' +
                '<h3>Available <span class="available-title">'+availableTitle+'</span></h3><hr />'+
                '<div class="scrollbar-macosx drag-avb-col" style="height: 100px; border: 2px dashed #eef0f3; overflow: auto; min-height: 100px; cursor:grab;"><div class="dragula" id="drag-members" style="cursor:grab;"></div></div>'+
            '</div>');
        let $team = $form.find('#drag-team'), $members = $form.find('#drag-members');
        $team.html('Loading...');
        $members.html('Loading...');
        $.post('/users/team/' + userId, { role: role }, function (resp) {
            if (resp.error) {
                $(window).trigger('user-form-hide');
                return CAMS.alert('Error', resp.msg);
            }
            // deconstruct from ease of use
            var u = resp.data;
            // load the members available
            $members.html('');
            u.members.forEach((user) => {
                user.avatar = user.avatar ? user.avatar : 'https://ui-avatars.com/api/?name=' + user.firstName + ' ' + user.lastName + '&background=' + getRandomColor() + '&size=200&color=000000';
                $members.append(
                    '<a href="javascript:void(0);" data-user-id="' + user._id + '" data-toggle="tooltip" title="' + user.firstName + ' ' + user.lastName + '" style="cursor:grab;">' +
                    '<img src="' + user.avatar + '" title="' + user.firstName + ' ' + user.lastName + '" class="rounded-circle m-1" width="32px"/></a>'
                );
            });
            // load the team
            $team.html('');
            u.team.forEach((user) => {
                user.avatar = user.avatar ? user.avatar : 'https://ui-avatars.com/api/?name=' + user.firstName + ' ' + user.lastName + '&background=' + getRandomColor() + '&size=200&color=000000';
                $team.append(
                    '<a href="javascript:void(0);" data-user-id="' + user._id + '" data-toggle="tooltip" title="' + user.firstName + ' ' + user.lastName + '" style="cursor:grab;">' +
                    '<img src="' + user.avatar + '" title="' + user.firstName + ' ' + user.lastName + '" class="rounded-circle m-1" width="32px" style="border: 1px solid #c1c1c1;"/></a>'
                );
                $members.find('a[data-user-id="' + user._id + '"]').remove();
            });
            
            var reportsTo = dragula({
                revertOnSpill: true
            }).on('drop', function (el, target, source, sibling) {
                // something goes here
            });
            $form.find('.dragula').each(function (i) {
                reportsTo.containers.push(this);
            });
            //$('[data-toggle="tooltip"]').tooltip(); // Not working here
        }, 'json').fail(function () {
            $(window).trigger('user-form-hide');
            addMsg('An unexpected error was encountered while trying to load your user. Please refresh the page to try again.');
        });
    }
    $('.permission-section').find('input[type="checkbox"]').on('change', function () {
        $(this).val($(this).prop('checked'));
    });
    function getRandomColor() {
        var letters = '0123456789ABCDEF';
        var color = '';
        for (var i = 0; i < 6; i++) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    }
    function rolePermissions(role, permissions = {}) {
        $.get('/account-management/permissions/roles/' + role, function (res) {
            if (res.error) { CAMS.alert('Error', res.msg, 'error'); }
            let perns = typeof permissions.dashboard != 'undefined' ? permissions : res.data.permissions;
            $.each(perns, function (key, value) {
                $.each(value, function (k, v) {
                    let elmId = 'user-permissions-' + key + '-' + k;
                    let chkElm = $('#modal-user-form').find('#' + elmId);
                    let lblElm = $('#modal-user-form').find('label[for="' + elmId + '"]');
                    if (chkElm) {
                        chkElm.prop('checked', v);
                        chkElm.val(v);
                        $('.permission-section').fadeIn(1, function () {
                            lblElm.addClass('animated zoomIn');
                        }).delay(10).show(0, function () {
                            lblElm.removeClass('animated zoomIn');
                        });                       
                    }
                });
            });
        });
    }
	
	$form.on('mouseover', '#exposure', function(e){
		e.preventDefault();
		$form.find('#user-password').attr('type', 'text');
		$form.find('#user-confirm').attr('type', 'text');
	  });
	  $form.on('mouseout', '#exposure', function(e){
		e.preventDefault();
		$form.find('#user-password').attr('type', 'password');
		$form.find('#user-confirm').attr('type', 'password');
	  });
	
});

On line #142, you see this:

$form.on('click', '.btn-change-password', function(){
      $(this).replaceWith( $passwordForm.clone() );
});


$passwordForm.clone() boils down to this:

$passwordForm = $form.find('.password-template').clone().removeClass('d-none'),

So, I'm completely cool with HOW the "change password" is being used to reveal those fields that are otherwise hidden.

But how can it also be used as a way to submit the form?

Perhaps it comes down to "click" versus "submit," but I don't know.

What do you think?
ASKER CERTIFIED SOLUTION
Join our community to see this answer!
Unlock 2 Answers and 3 Comments.
Start Free Trial
Learn from the best

Network and collaborate with thousands of CTOs, CISOs, and IT Pros rooting for you and your success.

Andrew Hancock - VMware vExpert
See if this solution works for you by signing up for a 7 day free trial.
Unlock 2 Answers and 3 Comments.
Try for 7 days

”The time we save is the biggest benefit of E-E to our team. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange.

-Mike Kapnisakis, Warner Bros