We help IT Professionals succeed at work.
Get Started

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

Bruce Gust
Bruce Gust asked
on
92 Views
Last Modified: 2020-03-04
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>

Open in new window


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');
	  });
	
});

Open in new window


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?
Comment
Watch Question
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2017
Commented:
This problem has been solved!
Unlock 2 Answers and 3 Comments.
See Answers
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE