Select Page

AngularJS Form Validation

Form validation is an important part of web development. The user experience of your online forms will leave an impression on your visitors so it is important that you build great forms on your website. We will apply AngularJS to an existing online form.

 

Initial Form

Use the following HTML which uses Bootstrap CSS for formatting as a starting point. After you work through this example you can apply the changes to an existing form.

<div class="form-container">
<form name="myform" action="" method="post">
 
 <div class="form-group">
 <label for="firstname">First Name &#42;</label>
 <input type="text" name="firstname" placeholder="First Name">
 </div><!-- .form-group -->
 
 <div class="form-group">
 <label for="lastname">Last Name &#42;</label>
 <input type="text" name="lastname" placeholder="Last Name">
 </div><!-- .form-group -->
 
 <div class="form-group">
 <label for="email">Email Address &#42;</label>
 <input type="text" name="email" placeholder="Email Address">
 </div><!-- .form-group -->
 
 <button type="submit" class="btn btn-default">Submit</button>
</form>
</div><!-- #form-container -->

 

Add AngularJS to your page.

Add AngularJS to the bottom of your page. I also added in the Bootstrap CDN for CSS styling. The ng-app directive tells AngularJS that this is the root element of the AngularJS application. We will apply this to the div outside our form. The “form-container” class is optional we are using it for our demo. You will want to apply ng-app to a div outside your existing form.

<div class="form-container" ng-app>
<form ...>
  ...
</form>
</div><!-- #form-container -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

 

Add form validation.

Apply the following angular directives for form validation.

The ng-required directive indicates that a field is required. If a required field has no value the form will automatically have an ng-invalid class applied until the requirements are met.

  <div class="form-group">
    <label for="firstname">First Name &#42;</label>
    <input type="text" name="firstname" placeholder="First Name"
      ng-required="true">
  </div><!-- .form-group -->

 

Test form values

The ng-model directive binds an input, select, textarea (or custom form control) value to angular. Use the same field name and append user.fieldname to the front to bind it to the {{user}} data object which we will use to view our form values.

  <div class="form-group">
    <label for="firstname">First Name &#42;</label>
    <input type="text" name="firstname" placeholder="First Name"
      ng-model="user.firstname"
      ng-required="true">
  </div><!-- .form-group -->

For testing we add an alert div to output our {{user}} data which will display our ng-model form values. Angular uses the double curly brackets to display data. Add this above the submit button.

 <p class="alert alert-warning">Output: {{user}}</p>

 

Validate the email address

The ng-pattern directive is used on the email field for regular expression matching to validate the email address.

  <div class="form-group">
    <label for="email">Email Address &#42;</label>
    <input type="text" name="email" placeholder="Email Address"
      ng-model="user.email"
      ng-required="true"
      ng-pattern="/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/">
  </div><!-- .form-group -->

 

Disable the submit button until the form passes validation

The ng-disabled directive is applied to our submit button and will remain disabled until all the form validation passes.

  <button type="submit" class="btn btn-default" ng-disabled="myform.$invalid">Submit</button>

 

Final touches

We are adding additional user prompts to let the user know there is missing field data.

  • The ng-show directive will display this element until the requirements indicated in the property are met.
  • The field was marked as required in the previous step by ng-required=”true” property. The myform.firstname.$invalid condition will return true until there is a value added into the field.
  • We also added a $touched condition which means the field has been activated. This will not return true until the cursor has entered the field.
 <div class="form-group">
 <label for="firstname">First Name &#42;</label>
 <input type="text" name="firstname" placeholder="First Name"
 ng-model="user.firstname"
 ng-required="true">
 <div ng-show="myform.firstname.$invalid && myform.firstname.$touched"
 class="alert alert-warning">You must fill out your first name.</div>
 </div><!-- .form-group -->

 

Completed Form

Here is the code for the completed form.

<div class="form-container" ng-app>
<form name="myform" action="" method="post">
 
 <div class="form-group">
 <label for="firstname">First Name &#42;</label>
 <input type="text" name="firstname" placeholder="First Name"
 ng-model="user.firstname"
 ng-required="true">
 <div ng-show="myform.firstname.$invalid && myform.firstname.$touched"
 class="alert alert-warning">You must fill out your first name.</div>
 </div><!-- .form-group -->
 
 <div class="form-group">
 <label for="lastname">Last Name &#42;</label>
 <input type="text" name="lastname" placeholder="Last Name"
 ng-model="user.lastname"
 ng-required="true">
 <div ng-show="myform.lastname.$invalid && myform.lastname.$touched" 
 class="alert alert-warning">You must fill out your last name.</div>
 </div><!-- .form-group -->
 
 <div class="form-group">
 <label for="email">Email Address &#42;</label>
 <input type="text" name="email" placeholder="Email Address"
 ng-model="user.email"
 ng-required="true"
 ng-pattern="/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/">
 <div ng-show="myform.email.$invalid && myform.email.$touched" 
 class="alert alert-warning">This must be a valid email.</div>
 </div><!-- .form-group -->
 
 <!-- Display Form Values for Testing !!!Delete after testing!!! -->
 <p class="alert alert-warning">Output: {{user}}</p>
 
 <button type="submit" class="btn btn-default" ng-disabled="myform.$invalid">Submit</button>
</form>
</div><!-- #form-container -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

Drupal 7 Security Configuration

Prevent Browser caching
Put this at the top of your theme template.php file:

header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

 

Download and enable the SecKit module:
https://www.drupal.org/project/seckit
There are some nice default settings. We can review tweak these settings as part of a security review when we have more time.

HTML Email Signatures

Creating an HTML email signature can be a deceivingly difficult process. After having to tackle this issue a number of times this is the best method I have found to get the correct formatting.

  1. Start with a clean HTML file. You only want the divs for your signature.
  2. Save to your desktop and view it in your browser.
  3. Use in-line styles and link to external images for any logos or graphics.
  4. When the format is correct you want to use a browser on Windows and Outlook.
  5. Select the formatted details in your browser then copy and paste it into Outlook. As you know Microsoft does preserve a lot of hidden formatting when you cut and paste into their products. You may have experience headaches with Word doing a lot of crazy formatting. But in this case it is helping us preserve the formatting for your signature.
  6. Send a test email out with your updated signature and view it in various email clients to confirm the formatting is correct.

Some Drupal SEO Considerations

Some SEO features you may want to include in your Drupal website.  Here are a few notes I took after conducting an SEO review on one of my websites.

Title Tag
A title tag is the main text that describes an online document. Title elements have long been considered one of the most important on-page SEO elements (the most important being overall content), and appear in three key places: browsers, search engine results pages, and external websites.

Here are my personal preferences for Configuration > Site information:

  • Site name: Project Name | ASU
    The page title will the render as [Page Title] | Project Name | ASU.

Metatag Description and Keywords
While most search engines now ignore the metatag keywords but the page description is still very important.  I add a teaser field to a node which I use to generate this content. Install the Metatag module.

  • Install the Metatag module with the follow submodules enabled: Metatag, Metatag verification (see below)
  • Click configuration on Metatag.
    • Global > Content select the Override link.
    • Set the Description field to your custom field in my case it is [node:field_news_teaser].
  • Site Ownership Verification
    • Enable the module Metatag verification (part of the Metatag package)
    • Follow the HOWTO links available on the documentation: https://www.drupal.org/node/1774342

XML Sitemap
The XML sitemap module creates a sitemap that conforms to the sitemaps.org specification. This helps search engines to more intelligently crawl a website and keep their results up to date. The sitemap created by the module can be automatically submitted to Ask, Google, Bing (formerly Windows Live Search), and Yahoo! search engines.

  • Enable the following modules included in the package: XML sitemap, XML sitemap custom, XML sitemap engines, XML sitemap menu, XML sitemap node
  • Update your content types to rank the priority of each: Structure > Content Types > Click Edit on your content type you want to be included in the site map.
  • On the bottom configuration tabs you will click XML sitemap. Use the following settings
    • Inclusion: Included
      This indicates that this content type will be included in the site map.
    • Default priority: 0.5 normal
      The priority is the importance value of your content. Basic pages I leave at the default 0.5 normal setting. News I rank at that same value. On one website I rank Research Centers at 0.8 because this content type is the most important for the website. The default setting for the home page is 1.0 which is the highest importance.
  • Configuration Settings: Go to Configuration > Search and Metadata Section > XML sitemap and review the settings. If everything looks good you can click Rebuild Links and run that to get your initial sitemap built.
  • Click the Search Engines tab and click the Bing and Google checkboxes and save the submission settings.

Copy Drupal site to local Dev

Local Development Environment: MAMP Pro

  1. Download a copy of your drupal public folder to your computer.
  2. Export a copy of your database.
  3. Create the database on your local machine.  Import your database from the file you just downloaded. For larger databases please reference my blog post about how to increase the memory allowance on MAMP.
  4. Copy the drupal root folder into your MAMP htdocs folder. Rename it to “dev.websitename” to keep things organized.
  5. Create your MAMP Host entries so your site will load at “http://websitename.local:8888”.
  6. Files to update:
    • .htaccess – update all references to the live URL and update them to your local URL.
    • settings.php – update all references to the live URL to your local URL. Set $cookie_domain to “localhost”.
      • update database settings
      • $base_url = “http://websitename.local:8888”
      • $cookie_domain = “localhost”
  7. Launch your site and everything should be working as expected.

Using MAMP Pro as a Local Development Server

MAMP Pro is great.  It does a number things well which helps streamline the development process. The primary reasons I use MAMP Pro is that I can pull my sites from git into my local MAMP server, import the database quickly (including large SQL files), and it renders right in my browser right away at http://dev-projectname:8888 with very little setup.  I will show you how to edit some configuration settings to get you setup and working quickly.

Setup your Dev environment:

  1. Using your Finder go to /Applications/MAMP/htdocs/ and create your Document Root folder for your project. Import your project files into this folder. Note: this htdocs folder is located at c:\MAMP\htdocs\ on the Windows version.
  2. Using MAMP Pro click the Hosts tab in MAMP Pro and click the + button on the bottom left.
  3. Add your development project details. I generally go with dev-projectname as the Server Name. This is what you will load in your browser to see your website some servers set this to localhost but I find dev-projectname is more useful for my purposes. The only other option you edit under Hosts is the Document Root which you set to the folder you created in Step 1.
  4. Click the Start icon in the top right and this gets your server running.
  5. Your website will now load in http://dev-projectname:8888.

The reason you want to use MAMP Pro is so you can develop your sites using http://dev-projectname:8888 rather than http://localhost/projectname:8888 which is a subdirectory and you run into issues with absolute URL paths. Just upgrade and use the Hosts feature. It’s so worth it.

Create/Import your Database:

  1. Click the MySQL tab and click phpMyAdmin to open that in your browser.
  2. Click the New link on the top left above the database list.
  3. Name your database dev_projectname.  You can name the database whatever you like but to keep it organized I name it the same as the Server Name I created in the first set of instructions. Note that I use an underscore for the database name and a hyphen for the project URL.
  4. Now you can import your database.
  5. Edit your website configuration files to connect to this local database.

Importing large .sql files using phpMyAdmin:  

If you are seeing this error “You probably tried to upload too large file. Please refer to documentation for ways to workaround this limit.” You need to edit your configuration files inside MAMP to import a database larger than 32MB.

  1. Go to the PHP tab in MAMP Pro.  Make note of the PHP version you are using.  I set my version to 5.6.x to match the version running on my Production Web Server.
  2. Go to File > Edit Template > PHP > select the version of PHP your server is running.  Edit the following settings and save:
    • post_max_size = 256M
    • upload_max_filesize = 256M
    • max_execution_time = 600
    • max_input_time = 600
    • memory_limit = 512M
  3. Restart your server and you should now be able to upload up to 256MB to your database.

Now that everything is running you should be able to quickly pull your sites from Github into your MAMP server; export your large database from Production and import it right into your local Dev; and have it all render at http://dev-projectname:8888 quickly. Make your edits and push your changes right onto QA and Production like a champ.

Plugin Review: Easy Table (WordPress)

There are a number of options for adding tables to your WordPress website.  The plugin I prefer is Easy Table because it is simple to setup and very functional.

Easy Table
Easy Table is WordPress plugin to create table in post, page, or widget in easy way using CSV format.
https://wordpress.org/plugins/easy-table/

 

Settings

Before getting started the only setting I change is switching the delimiter from a comma (,) to a pipe (|) since commas are used to display some data.  It prevents you from accidently running into formatting errors as demonstrated in my example below which has a list of cities using a comma before the state.

delimiter

 

Usage

Here is a sample table using this plugin.  The first row will be the table headers which is a setting inside the plug options which can be disabled.

sample-data

 

Rendered Table

Name Age Gender Cities Lived
Norman M. Patton 35 Male  Waipahu, HI
John T. Bowman 27 Male  Butler, MO
Geraldine F. Crawford 32 Female  Sheridan, WY
Benjamin B. Waugh 26 Male  Pontiac, MI
Jill D. Rivers 26 Female  Jarrettsville, MD

Twitter Bootstrap – Improved Carousels

Twitter Bootstrap is great but their carousels always bugged me.  They don’t work well with images that are not the exact same size and clients uploading their own graphics you end up with a slider that resizes depending on the current image that is loaded.

My solution (using Bootstrap 3):

  • Replace the image with a container div using the image file as a background.
  • Set width to 100% so it will expand to fill it’s container.  Set the height of the slider so it will have a consistent display regardless of the image size.
  • Win

Here is the sample page comparing the two:
http://parkhurstdesign.com/sites/bootstrapsandbox/slider-fixed.php

Sample Code from the Twitter Bootstrap website:

<div id="carousel-example" class="carousel slide" data-ride="carousel">
  <!-- Indicators -->
  <ol class="carousel-indicators">
    <li data-target="#carousel-example" data-slide-to="0" class="active"></li>
    <li data-target="#carousel-example" data-slide-to="1"></li>
    <li data-target="#carousel-example" data-slide-to="2"></li>
  </ol>

  <!-- Wrapper for slides -->
  <div class="carousel-inner">
    <div class="item active">
      <img src="..." alt="...">
      <div class="carousel-caption">
        ...
      </div>
    </div>
    ...
  </div>

  <!-- Controls -->
  <a class="left carousel-control" href="#carousel-example" data-slide="prev">
    <span class="glyphicon glyphicon-chevron-left"></span>
  </a>
  <a class="right carousel-control" href="#carousel-example" data-slide="next">
    <span class="glyphicon glyphicon-chevron-right"></span>
  </a>
</div>

The only change is the “item” divs. I am using this in a CMS so I use inline styles for the background divs so I can set the background image files within the loop.  Just easier for my purposes.  Change the img tag to a div as follows:

<div class="item active">
  <img src="/path/image.jpg" alt="blah">
  <div style="background:url(/path/image.jpg) center center; 
          background-size:cover;" class="slider-size">
    <div class="carousel-caption">
      <h2>Headline</h2>
      <p>Content text to go here. </p>
    </div>
  </div>
</div>

The CSS:

.slider-size {
height: 400px; /* This is your slider height */
}
.carousel {
width:100%; 
margin:0 auto; /* center your carousel if other than 100% */ 
}

One more thing.  The damn sample script navigation doesn’t work on the Bootstrap website for some CMS themes.  Code changes in bold.  Here is how you fix it:

<!-- Controls -->
<a class="left carousel-control" href="javascript:void(0)" 
     data-slide="prev" data-target="#carousel-example">
<span class="glyphicon glyphicon-chevron-left"></span>
</a>
<a class="right carousel-control" href="javascript:void(0)" 
     data-slide="next" data-target="#carousel-example">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>

 Accessibility concerns: with an img tag you include alt text for accessibility.  Divs unfortunately do not allow you this option.  It is a trade-off but you do include caption text within the div which helps but I am sure it is weighted differently.

Return hours using PHP DateTime

There are a number of ways to find the difference between two time stamps in PHP.  I needed to find the number of hours for a project with a time clock.  Here is an example you can test using the DateTime class.

		$date1 = new DateTime('2004-03-01T01:30:00');
		$date2 = new DateTime('2004-03-02T02:30:00');
		
		$diff = $date2->diff($date1);
		
		$hours = $diff->h;
		$hours = $hours + ($diff->format('%d')*24);
		$hours = $hours + ($diff->format('%i')/60); 
		
                // output our result with 2 decimal places
		printf('Hours: %s',number_format($hours,2));

You can edit the date values to test the result.  It will also take into account leap years if you want to be assured of the results.