emGee Software Solutions Custom Database Applications

Share this

Web Design

Introduction to Forms in Angular 4: Template-Driven Forms

Tuts+ Code - Web Development - Mon, 12/18/2017 - 04:00
What You'll Be Creating

Forms are critical to any modern front-end application, and they're a feature that we use every day, even if don't realize it. Forms are required for securely logging in a user to the app, searching for all the available hotels in a particular city, booking a cab, building a to-do list, and doing tons of other things that we are used to. Some forms have just a couple of input fields, whereas other forms could have an array of fields that stretch to a couple of pages or tabs. 

In this tutorial, we will be talking about different strategies available for developing forms in Angular. Irrespective of the strategy that you choose, here are the things that a form library should cover:

  • Support two-way binding so that the input control values are in sync with the component state.
  • Keep track of the form state and use visual cues to let the user know whether the current state is valid or not. For instance, if the username has invalid characters, a red border should appear around the input field for the username.
  • Have a mechanism to display validation errors properly.
  • Enable or disable certain parts of the form unless some validation criteria are met.
Introduction to Forms in Angular

Angular, being a full-fledged front-end framework, has its own set of libraries for building complex forms. The latest version of Angular has two powerful form-building strategies. They are:

  • template-driven forms 
  • model-driven or reactive forms

Both the technologies belong to the @angular/forms library and are based on the same form control classes. However, they differ remarkably in their philosophy, programming style, and technique. Choosing one over the other depends on your personal taste and also on the complexity of the form that you are trying to create. In my opinion, you should try both the approaches first and then choose one that fits your style and the project at hand. 

The first part of the tutorial will cover template-driven forms with a practical example: building a signup form with validation for all form fields. In the second part of this tutorial, we will retrace the steps to create the same form using a model-driven approach instead. 

Template-Driven Forms

The template-driven approach is a strategy that was borrowed from the AngularJS era. In my opinion, it is the most straightforward method for building forms. How does it work? We will be using some Angular directives. 

Directives allow you to attach behavior to elements in the DOM.
— Angular Documentation

Angular provides form-specific directives that you can use to bind the form input data and the model. The form-specific directives add extra functionality and behavior to a plain HTML form. The end result is that the template takes care of binding values with the model and form validation. 

In this tutorial, we will be using template-driven forms to create the signup page of an application. The form will cover the most common form elements and different validation checks on these form elements. Here are the steps that you will follow in this tutorial.

  • Add FormsModule to app.module.ts.
  • Create a class for the User model.
  • Create initial components and layout for the signup form.
  • Use Angular form directives like ngModel, ngModelGroup, and ngForm.
  • Add validation using built-in validators.
  • Display validation errors meaningfully.
  • Handle form submission using ngSubmit.

Let's get started.

Prerequisites

The code for this project is available on my GitHub repo. Download the zip or clone the repo to see it in action. If you prefer to start from scratch instead, make sure that you have Angular CLI installed. Use the ng command to generate a new project. 

$ ng new SignupFormProject

Next, generate a new component for the SignupForm.

ng generate component SignupForm

Replace the contents of app.component.html with this:

<app-signup-form> </app-signup-form>

Here is the directory structure for the src/ directory. I've removed some non-essential files to keep things simple.

. ├── app │   ├── app.component.css │   ├── app.component.html │   ├── app.component.ts │   ├── app.module.ts │   ├── signup-form │   │   ├── signup-form.component.css │   │   ├── signup-form.component.html │   │   └── signup-form.component.ts │   └── User.ts ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css ├── tsconfig.app.json └── typings.d.ts

As you can see, a directory for the SignupForm component has been created automatically. That's where most of our code will go. I've also created a new User.ts for storing our User model.

The HTML Template

Before we dive into the actual component template, we need to have an abstract idea of what we are building. So here is the form structure that I have in my mind. The signup form will have several input fields, a select element, and a checkbox element. 


Here is the HTML template that we will be using for our registration page. 

HTML Template <div class="row custom-row"> <div class= "col-sm-5 custom-container jumbotron"> <form class="form-horizontal"> <fieldset> <legend>SignUp</legend> <!--- Email Block ---> <div class="form-group"> <label for="inputEmail">Email</label> <input type="text" id="inputEmail" placeholder="Email"> </div> <!--- Password Block ---> <div class="form-group"> <label for="inputPassword">Password</label> <input type="password" id="inputPassword" placeholder="Password"> </div> <div class="form-group"> <label for="confirmPassword" >Confirm Password</label> <input type="password" id="confirmPassword" placeholder="Password"> </div> <!--- Select gender Block ---> <div class="form-group"> <label for="select">Gender</label> <select id="select"> <option>Male</option> <option>Female</option> <option>Other</option> </select> </div> <!--- Terms and conditions Block ---> <div class="form-group checkbox"> <label> <input type="checkbox"> Confirm that you've read the Terms and Conditions </label> </div> <!--- Buttons Block ---> <div class="form-group"> <button type="reset" class="btn btn-default">Cancel</button> <button type="submit" class="btn btn-primary">Submit</button> </div> </fieldset> </form> </div> </div>

The CSS classes used in the HTML template are part of the Bootstrap library used for making things pretty. Since this is a not a design tutorial, I won't be talking much about the CSS aspects of the form unless necessary. 

Basic Form Setup

To use the template-driven form directives, we need to import the FormsModule from @angular/forms and add it to the imports array in app.module.ts.

app/app.module.tsimport { FormsModule } from '@angular/forms'; @NgModule({ . . imports: [ BrowserModule, FormsModule ], . . }) export class AppModule { }

Next, create a class that will hold all properties of the User entity. We can either use an interface and implement it in the component or use a TypeScript class for the model.

app/User.tsexport class User { id: number; email: string; //Both the passwords are in a single object password: { pwd: string; confirmPwd: string; }; gender: string; terms: boolean; constructor(values: Object = {}) { //Constructor initialization Object.assign(this, values); } }

Now, create an instance of the class in the SignupForm component. I've also declared an additional property for the gender. 

app/signup-form/signup-form.component.tsimport { Component, OnInit } from '@angular/core'; // Import the User model import { User } from './../User'; @Component({ selector: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css'] }) export class SignupFormComponent implements OnInit { //Property for the gender private gender: string[]; //Property for the user private user:User; ngOnInit() { this.gender = ['Male', 'Female', 'Others']; //Create a new user object this.user = new User({ email:"", password: { pwd: "" , confirm_pwd: ""}, gender: this.gender[0], terms: false}); } }

For the signup-form.component.html file, I am going to use the same HTML template discussed above, but with minor changes. The signup form has a select field with a list of options. Although that works, we will do it the Angular way by looping through the list using the ngFor directive.

app/signup-form/signup-form.component.html<div class="row custom-row"> <div class= "col-sm-5 custom-container jumbotron"> <form class="form-horizontal"> <fieldset> <legend>SignUp</legend> . . <!--- Gender Block --> <div class="form-group"> <label for="select">Gender</label> <select id="select"> <option *ngFor = "let g of gender" [value] = "g"> {{g}} </option> </select> </div> . . </fieldset> </form> </div> </div>

Next, we want to bind the form data to the user class object so that when you enter the signup data into the form, a new User object is created that temporarily stores that data. This way, you can keep the view in sync with the model, and this is called binding. 

There are a couple of ways to make this happen. Let me first introduce you to ngModel and ngForm.

ngForm and ngModel

ngForm and ngModel are Angular directives that are essential to creating template-driven forms. Let's start with ngForm first. Here is an excerpt about ngForm from the Angular docs.

The NgForm directive supplements the form element with additional features. It holds the controls you created for the elements with an ngModel directive and name attribute, and monitors their properties, including their validity. It also has its own valid property which is true only if every contained control is valid.

First, update the form with the ngForm directive:

app/signup-form/signup-form.component.html<form class="form-horizontal" #signupForm = "ngForm"> . . </form>

#signupForm is a template reference variable that refers to the ngForm directive which governs the entire form. The example below demonstrates the use of a ngForm reference object for validation.

app/signup-form/signup-form.component.html<button type="submit" class="btn btn-success" [disabled]="!signupForm.form.valid"> Submit </button>

Here, signupForm.form.valid will return false unless all the form elements pass their respective validation checks. The submit button will be disabled until the form is valid.  

As for binding the template and the model, there are plenty of ways to do this, and ngModel has three different syntaxes to tackle this situation. They are:

  1. [(ngModel)] 
  2. [ngModel]
  3. ngModel

Let's start with the first one.

Two-Way Binding Using [(ngModel)]

[(ngModel)] performs two-way binding for reading and writing input control values. If a [(ngModel)] directive is used, the input field takes an initial value from the bound component class and updates it back whenever any change to the input control value is detected (on keystroke and button press). The image below describes the two-way binding process better.

Here is the code for the email input field:

<div class="form-group"> <label for="inputEmail">Email</label> <input type="text" [(ngModel)] = "user.email" id="inputEmail" name="email" placeholder="Email"> </div>

[(ngModel)] = "user.email" binds the user's email property to the input value. I've also added a name attribute and set name="email". This is important, and you will get an error if you've not declared a name attribute while using ngModel. 

Similarly, add a [(ngModel)] and a unique name attribute to each form element. Your form should look something like this now:

app/signup-form/signup-form.component.html. . . <div ngModelGroup="password"> <div class="form-group" > <label for="inputPassword">Password</label> <input type="password" [(ngModel)] = "user.password.pwd" name="pwd" placeholder="Password"> </div> <div class="form-group"> <label for="confirmPassword" >Confirm Password</label> <input type="password" [(ngModel)] = "user.password.confirmPwd" name="confirmPwd" placeholder="Confirm Password"> </div> </div> <div class="form-group"> <label for="select">Gender</label> <select id="select" [(ngModel)] = "user.gender" name = "gender"> <option *ngFor = "let g of gender" [value] = "g"> {{g}} </option> </select> </div> . . .

The ngModelGroup is used to group together similar form fields so that we can run validations only on those form fields. Since both the password fields are related, we will put them under a single ngModelGroup. If everything is working as expected, the component-bound user property should be in charge of storing all the form control values. To see this in action, add the following after the form tag:

{{user | json}}

Pipe the user property through the JsonPipe to render the model as JSON in the browser. This is helpful for debugging and logging. You should see a JSON output like this. 

The values are flowing in from the view to the model. What about the other way around? Try initializing the user object with some values.

app/signup-form/signup-form.component.tsthis.user = new User({ //initialized with some data email:"thisisfromthemodel@example.com", password: { pwd: "" , confirm_pwd: ""}, gender: this.gender[0] });

And they automatically appear in the view:

{ "email": "thisisfromthemodel@example.com", "password": { "pwd": "", "confirm_pwd": "" }, "gender": "Male" }

The two-way binding [(ngModel)] syntax helps you build forms effortlessly. However, it has certain drawbacks; hence, there is an alternate approach that uses ngModel or [ngModel].

Adding ngModel to the Mix

When ngModel is used, we are in fact responsible for updating the component property with the input control values and vice versa. The input data doesn't automatically flow into the component's user property.

So replace all instances of [(ngModel)] = " " with ngModel. We will keep the name attribute because all three versions of ngModel need the name attribute to work. 

app/signup-form/signup-form.component.html<div class="form-group"> <label for="inputEmail">Email</label> <input type="text" ngModel id="inputEmail" name="email" placeholder="Email"> </div>

With ngModel, the value of the name attribute will become a key of the ngForm reference object signupForm that we created earlier. So, for example, signupForm.value.email will store the control value for the email id. 

Replace {{user | json}} with {{signupForm.value | json }} because that's where all the state is stored right now. 

One-Way Binding Using [ngModel]

What if you need to set the initial state from the bound class component? That's what the [ngModel] does for you. 

Here the data flows from the model to the view. Make the following changes to the syntax to use one-way binding:

app/signup-form/signup-form.component.html<div class="form-group"> <label for="inputEmail">Email</label> <input type="text" [ngModel] = "user.email" id="inputEmail" name="email" placeholder="Email"> </div>

So which approach should you choose? If you're using [(ngModel)] and ngForm together, you will eventually have two states to maintain—user and signupForm.value—and that could be potentially confusing. 

{ "email": "thisisfromthemodel@example.com", "password": { "pwd": "thisispassword", "confirm_pwd": "thisispassword" }, "gender": "Male" } //user.value { "email": "thisisfromthemodel@example.com", "password": { "pwd": "thisispassword", "confirm_pwd": "thisispassword" }, "gender": "Male" } //signupForm.value

Hence, I will recommend using the one-way binding method instead. But that's something for you to decide.

Validation and Displaying Error Messages 

Here are our requirements for the validation.

  • All form controls are required.
  • Disable the submit button until all input fields are filled.
  • The email field should strictly contain an email id.
  • The password field should have a minimum length of 8.
  • Both the password and confirmation should match.
Our form with validation in place

The first one is easy. You have to add a required validation attribute to each form element like this:

app/signup-form/signup-form.component.html<input type="text" [ngModel] = "user.email" name="email" #email = "ngModel" placeholder="Email" required>

Apart from the required attribute, I've also exported a new #email template reference variable. This is so that you can access the input box's Angular control from within the template itself. We will use it to display errors and warnings. Now use the button's disabled property to disable the button:

app/signup-form/signup-form.component.html<button type="submit" class="btn btn-success" [disabled]="!signupForm.form.valid"> Submit </button>

To add a constraint on email, use the pattern attribute that works with input fields. Patterns are used to specify regular expressions like the one below:

pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$"

For the password field, all you have to do is add a minlength=" " attribute:

app/signup-form/signup-form.component.html <input type="password" ngModel id="inputPassword" name="pwd" #pwd = "ngModel" placeholder="Password" minlength="8" required>

To display the errors, I am going to use the conditional directive ngIf on a div element. Let's start with the input control field for email:

app/signup-form/signup-form.component.html<div class="form-group"> <label for="inputEmail">Email</label> <input type="text" [ngModel] = "user.email" name="email" #email = "ngModel" id="inputEmail" placeholder="Email" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$" required> </div> <!-- This is the error section --> <div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger"> <div *ngIf = "email.errors?.required"> Email field can't be blank </div> <div *ngIf = "email.errors?.pattern && email.touched"> The email id doesn't seem right </div> </div>

There is a lot going on here. Let's start with the first line of the error section.

<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">

Remember the #email variable that we exported earlier? It carries some amount of information about the input control state of the email field. This includes: email.valid, email.invalid, email.dirty, email.pristine, email.touched, email.untouched, and email.errors.  The image below describes each of those properties in detail.

So the div element with the *ngIf will be rendered only if the email is invalid. However, the user will be greeted with errors about the input fields being blank even before they have a chance to edit the form. 

To avoid this scenario, we've added the second condition. The error will be displayed only after the control has been visited or the control's value has been changed.

The nested div elements are used to cover all the cases of validation errors. We use email.errors to check all possible validation errors and then display them back to the user in the form of custom messages. Now, follow the same procedure for the other form elements. Here is how I've coded the validation for the passwords. 

app/signup-form/signup-form.component.html <div ngModelGroup="password" #userPassword="ngModelGroup" required > <div class="form-group"> <label for="inputPassword">Password</label> <input type="password" ngModel name="pwd" id="inputPassword" placeholder="Password" minlength ="8" required> </div> <div class="form-group"> <label for="confirmPassword" >Confirm Password</label> <input type="password" ngModel name="confirmPwd" id="confirmPassword" placeholder="Confirm Password"> </div> <div *ngIf="(userPassword.invalid|| userPassword.value?.pwd != userPassword.value?.confirmPwd) && (userPassword.touched)" class="alert alert-danger"> <div *ngIf = "userPassword.invalid; else nomatch"> Password needs to be more than 8 characters </div> <ng-template #nomatch > Passwords don't match </ng-template> </div> </div>

This is starting to look a bit messy. Angular has a limited set of validator attributes: required, minlength, maxlength, and pattern. To cover any other scenario like that of password comparison, you will have to rely on nested ngIf conditionals as I did above. Or ideally, create a custom validator function, which I will cover in the third part of this series.

In the code above, I've used the ngIf else syntax which was introduced in the latest version of Angular. Here is how it works:

<div *ngIf="isValid;else notvalid"> Valid content... </div> <ng-template #notValid>Not valid content...</ng-template>Submit the Form Using ngSubmit

We have nearly finished the form. Now we need to be able to submit the form, and the control of the form data should be handed over to a component method, say onFormSubmit().

app/signup-form/signup-form.component.ts<form novalidate (ngSubmit)="onFormSubmit(signupForm)" #signupForm="ngForm"> ...

Now, for the component:

app/signup-form/signup-form.component.ts... public onFormSubmit({ value, valid}: { value: User, valid: boolean }) { this.user = value; console.log( this.user); console.log("valid: " + valid); } ...Final Demo

Here is the final version of the application. I've added a few bootstrap classes to make the form pretty.

Summary

We're all done here. In this tutorial, we covered everything that you need to know about creating a form in Angular using the template-driven approach. Template-driven forms are popular for their simplicity and ease of use. 

However, if you need to build a form with lots of form elements, this approach will become messy. So, in the next tutorial, we will cover the model-driven way of building the same form. 

Share your thoughts in the comments below.

Categories: Web Design

UX Design Tips For Ecommerce Product Pages

A brilliant product page is the first step to higher conversions. Every eCommerce site exists to make money so you want to design a site that’s easy to use...

The post UX Design Tips For Ecommerce Product Pages appeared first on Onextrapixel.

Categories: Web Design

☑️ The Ultimate Digital Clean-Up Checklist: Are You Prepared For The New Year?

Smashing Magazine - Sat, 12/16/2017 - 08:32
With a couple of days left until New Year’s Eve, it’s just about time to set aside 60 minutes to clean up, sort out and back up your digital footprint, to ensure a good smooth start to 2017. So many little details tend to get forgotten or overlooked every single time, only to get fixed hastily later — but doesn’t it just feel right when everything is in the right place, neatly organized, even if you aren’t a compulsory cleaner or an obsessed perfectionist?
Categories: Web Design

The Smashing Mystery Riddle Is Resolved: When Purple Rain Meets Felix The Cat

Smashing Magazine - Fri, 12/15/2017 - 08:54
These riddles can be quite addictive and annoying, can’t they? With seven mischievous riddles published over the last few years, we’ve learned a few lessons along the way. At this point, you might be used to endless, mischievous, tricky, mean, time-consuming and intricate Mystery Riddles, and the latest one wasn’t any different. It had to be playful and challenging, and take at least 45 mins to resolve. In the end, the first answer to the new riddle came in after 1h 48minafter the (slightly delayed — sorry about that!
Categories: Web Design

Monthly Web Development Update 12/2017: Pragmatic Releasing, Custom Elements, And Making Decisions

Smashing Magazine - Fri, 12/15/2017 - 04:18
Editor’s Note: What happened in the web community in the last few weeks? Anselm Hannemann summarizes everything that’s new and important so that you don’t miss out on anything. Enjoy! Today I read an eye-opening article about the current young generation and their financial future. It’s hard to grasp words like “Millenials”, and there’s much talk about specific issues they face, but, for many of us, it’s not easy to understand their struggle — no matter if you’re older or younger than me (I qualify under the Millenial generation).
Categories: Web Design

#NoHacked 3.0: Tips on prevention

Google Webmaster Central Blog - Thu, 12/14/2017 - 16:47

Last week on #NoHacked, we have shared on hack detection and the reasons why you might get hacked. This week we focus on prevention and here are some tips for you!

  • Be mindful of your sources! Be very careful of a free premium theme/plugin!

You probably have heard about free premium plugins! If you've ever stumbled upon a site offering you plugins you normally have to purchase for free, be very careful. Many hackers lure you in by copying a popular plugin and then add backdoors or malware that will allow them to access your site. Read more about a similar case on the Sucuri blog. Additionally, even legit good quality plugins and themes can become dangerous if:

  • you do not update them as soon as a new version becomes available
  • the developer of said theme or plugin does not update them, and they become old over time.
In any case, keeping all your site's software modern and updated is essential in keeping hackers out of your website.

  • Botnet in wordpress A botnetis a cluster of machines, devices, or websites under the control of a third party often used to commit malicious acts, such as operating spam campaigns, clickbots, or DDoS. It's difficult to detect if your site has been infected by a botnet because there are often no specific changes to your site. However, your site's reputation, resources, and data are at risk if your site is in a botnet. Learn more about botnets, how to detect them, and how they can affect your site at Botnet in wordpress and joomla article.

As usual if you have any questions post on our Webmaster Help Forums for help from the friendly community and see you next week!

.blgimg1 { width: 100%; padding: 0 0 -10px 0; margin: 0; border: 0; float: left; } .blgimg2 { width: 100%; padding: 0 0 -10px 0; margin: 0; border: 0; float: left; } .blgimg3 { width: 100%; padding: 0 0 -10px 0; margin: 0; border: 0; } .blgimg4 { width: 100%; padding: 0 0 -10px 0; margin: 0; border: 0; }
Categories: Web Design

The Top 3 Ecommerce Sites of 2017 -- and What You Can Learn from Them

Entrepreneur: Latest Web Design Articles - Thu, 12/14/2017 - 08:30
What design features make these sites close sales?
Categories: Web Design

An Introduction To Automated Testing Of WordPress Plugins With PHPUnit

Smashing Magazine - Thu, 12/14/2017 - 06:38
WordPress is a popular content management system for building websites because it is easy to get started with and a ton of themes and plugins are available to extend its feature set. The main reason WordPress has a lot of plugins and themes is because it's easy for developers of any level to start building one. Most of its developers are not experienced, and they do not write tests for their work, perhaps because of the following reasons:
Categories: Web Design

Review: WrapKit Complete UI Kit for Bootstrap 4

Bootstrap is a responsive front-end framework that allows developers to rapidly prototype modern websites and applications. Thanks to its numerous features, it’s the top one choice of web developers...

The post Review: WrapKit Complete UI Kit for Bootstrap 4 appeared first on Onextrapixel.

Categories: Web Design

How Do I Know If Venture Capital Fits My Business?

Smashing Magazine - Wed, 12/13/2017 - 13:21
There is a popular image in the world of software which many young and inexperienced entrepreneurs are becoming infatuated with. It’s the idea that when you come up with an awesome idea, the highest peak to strive for — the ultimate goal — is getting in front of a venture capitalist and receiving a huge lump sum to propel your business to unimaginable heights and bring tremendous personal wealth. Well then, let’s explore what it really means to fund your business with equity capital.
Categories: Web Design

Get Rid of Bugs Quickly Using BugReplay

Tuts+ Code - Web Development - Wed, 12/13/2017 - 04:00

As a web developer, you are bound to come across clients who are not very tech savvy. Some clients might want you to create a new website for them, while others might want to only make changes to specific sections or add new functionality. This is how the situation generally plays out:

  1. You update a section of the website, check that everything is working fine, and then go to bed.
  2. Next day, you wake up only to find an email or message from your client which says that the website is not working as expected.
  3. You start wondering what it is that's not working and open the website on your own device. At this point, you may be able to reproduce the issue the client is talking about and fix it. However, in some unfortunate cases, the website would seem to be working correctly, and you won't be able to see what the client is complaining about.
  4. This is when the trouble begins. The website may not be working for your client as well as many other people, but you have no idea what's wrong and how to resolve the issue.
  5. You start asking the client what's not working, the browser they are using, exactly what they were doing, etc., in the hope of being able to reproduce the issue on your end. Sometimes you can reproduce the issue, but sometimes you can't.

The whole process is very frustrating for both you and your clients. In situations like these, I wish that I could just sit right next to the client and see everything myself. 

BugReplay helps you do something very similar. You no longer have to ask your users tons of questions about their browsers, cookies, plugins, etc. All you have to do is give them a link that they can click on to start browser recording.

You can use BugReplay for internal testing as well. In this article, we will go over all the features of this software that can make the internal testing of a website or providing awesome customer support a breeze.

BugReplay Records Everything Visible in the Browser Tab

Whenever you ask your users the steps they followed in order to reproduce a bug, you can never be sure that the users have not missed any steps. BugReplay helps you overcome this very common problem by recording everything the users did while trying to reproduce the bug. This is not only helpful when resolving customer complaints, but it also speeds up internal testing and makes the whole process very efficient.

As you can see in this video, BugReplay records every action the user takes, from button clicks to scrolling up and down the browser window.

While seeing what the user did when they ran into trouble with your website is helpful, it is still hard to know if any plugin installed by the user interfered with a script or if the user has disabled cookies, etc. Just by watching the above video, you may conclude that the images are not showing up because the call to retrieve the images failed. 

It is also possible that the images were retrieved without any error, but they were not displayed because of some issue with the code that was supposed to arrange them in two different columns.

All you can do at this point is just guess and wish there was a way for you to see all the network calls as well as the JS console to determine what went wrong. Luckily, BugReplay also records all the network requests and responses. 

These calls are all synchronized with the screen recording to help you figure out exactly when a network call was fired. The calls can be filtered based on a variety of factors like the domain, MIME type and HTTP status of the request, etc. The following video shows you how BugReplay can help you in quickly figuring out if any of the network calls caused the bug.

In the video, try to observe how clicking on the Settings icon on the right and applying different filters changes the list of requests shown in the Network Traffic Tab. Only the highlighted filters are applied at any given time. You should also see that the Network Traffic tab doesn't show any calls at the beginning. This is because a call is added to the tab only after a request has been made. Knowing when a call was fired goes a long way in figuring out what went wrong with the app or website.

The filters can prove very helpful when you are dealing with websites that can make a large number of requests with varying HTTP status codes and MIME types. In the following image, I have shown the result of filtering the requests by domain and selected localhost. As you can see, our network call was fired as expected.

You can also click on any network call to find out more about it, like the duration of the request. You can also check the request and response headers. 

The following screenshot shows the details of our second request. As you can see, we are getting the JSON response back. This means that something must have been wrong with the code that was supposed to place the images in two different columns.


Just like filtering network requests, you can also filter all the console messages under the JavaScript Logs tab based on their domain, level, or source. In this case, we are looking for an error message, and that's why we set the level filter to error. Once you click on the error that you want to inspect, you will see more details about it. Here is a screenshot of the error which was preventing the images from displaying in our case.

Thanks to BugReplay, we now know the library as well as the exact function which prevented our images from displaying. You can see how easy the software makes it for developers to debug their websites.

Internal Testing and Customer Support

It doesn't matter if you are using BugReplay for internal testing or customer support. The software records all the network calls and console logs in the same manner. However, keep in mind that you (or your users) need to use different browser extensions for these purposes, and the process is slightly different as well.

Here are the steps to follow when using BugReplay for internal testing:

  1. The first thing that you need to do is register and create an account. You can use the software for free for the first 30 days. 
  2. In the next step, you can download the BugReplay extension for Chrome. If you primarily use Firefox for your work, you can download the BugReplay Firefox extension instead.
  3. Once you have installed the extension, you can just click on the Start Recording button inside it to record a bug. 
  4. BugReplay also allows you to replay the video before saving it in order to make sure that the video captures all the necessary details properly. You can give each recording an optional title and description in order to identify it later.

Instead of recording a video, you can also take one or multiple screenshots in order to point out a bug in the website.

If your users want to report a bug in your website, they will have to download another extension called Feedback by BugReplay. First, you will have to generate a link from which the users can download the extension. The link can be generated by clicking on the Request Feedback Report tab in your account. 

You can give the link a title and an expiration date as well. After successful installation, the extension gives the users some easy-to-follow instructions so that they can record the bug with ease. The users can also optionally provide a title and description for the bug along with an email address for any further communications.

Since BugReplay records everything from the steps taken by a user to the network calls and the console logs, it becomes a lot easier for your users to submit detailed bug reports without getting frustrated with tons of questions. I would also like to point out that the Feedback by BugReplay extension doesn't give the users access to any of your other videos or to the dashboard. 

More BugReplay Features and Integrations

If you decide to use BugReplay for internal testing, you will probably be working with a team. Everyone in a team can access each other's reports, making it easy for them to work together in order to resolve bugs. You can invite other people to join your team by clicking on your name in the upper right corner of your BugReplay account page and then entering the names and email addresses of people that you want to invite.

Sometimes, you might want to share a bug report with people who are not in your team. BugReplay allows you to create a shareable URL for each of your videos and then make the video accessible to anyone with a link. You can share a video by clicking the share link next to that video in your dashboard. Once the concerned people have watched the video, you can revoke the shared URL by clicking on Unshare.

If you and your team use either GitHub or Jira, it makes sense to allow you to file issues with both of them directly using BugReplay. The BugReplay team has already written a detailed article that outlines all the necessary instructions to integrate JIRA

Integrating BugReplay with GitHub is easy as well. All you have to do is click on Notifications and Integrations on your account page and then select GitHub Issue Tracker under Add Integration. Similarly, you can integrate BugReplay with Slack by following the on-screen instructions.

If you want to receive an email whenever your users submit a bug report, you can provide BugReplay with one or more email addresses through which you want to be notified.

All these features of BugReplay make it one of the best tools to internally test your website and create detailed reports that are accessible to everyone on the team. Here is a testimonial from one of the many happy users of BugReplay.

BugReplay has greatly reduced my team's need to type tedious Steps to Reproduce and nearly eliminated uncertainty in bug reports - our dev team can start troubleshooting right away using BugReplay.
— Matt Viirlee, Project ManagerFinal Thoughts

BugReplay makes it very easy for you to provide awesome customer support to your customers and significantly speed up internal testing by providing some useful tools. Any bug report created by either your users or a member of the team will be visible to the whole team now. The ability to see all the network calls and the console log will give you the most detailed bug reports possible, without bothering your client with a single question about the bug.

If you have been looking for a tool that can help your users or team create detailed bug reports, BugReplay is probably going to satisfy all your needs. Since you can register for a BugReplay account without paying anything for the first 30 days, I would suggest that you give it a try. If things work out well, you will have an amazing tool that keeps both your clients and internal testing teams happy.

Categories: Web Design

How To Use Underlined Text To Improve User Experience

Smashing Magazine - Wed, 12/13/2017 - 01:03
(This article is supported by Adobe.) An underline is a horizontal line immediately below a portion of text. In our everyday experience, we underline to emphasize key sections of text, sometimes drawing an underline by hand below printed text. But underlines have their own place in the world of digital design. In fact, underlined text has become one of the most common, most recognizable features of our online experience. When we see an underlined word or sentence on a web page, we immediately assume it’s a link.
Categories: Web Design

A revamped SEO Starter Guide

Google Webmaster Central Blog - Tue, 12/12/2017 - 08:30
There are lots of resources out there to create great websites. Website owners often ask Google what our recommended practices are to make sure great websites are search-engine-friendly. Traditionally, our resources for beginners were the SEO Starter Guide and the Webmaster Academy. To help webmasters create modern, search-engine-friendly websites, we’re announcing today the launch of a new, updated SEO Starter Guide.

The traditional SEO Starter Guide lists best practices that make it easier for search engines to crawl, index and understand content on websites. The Webmaster Academy has the information and tools to teach webmasters how to create a site and have it found in Google Search. Since these two resources have some overlapping purpose and content, and could be more exhaustive on some aspects of creating a user friendly and safe website, we’re deprecating the Webmaster Academy and removing the old SEO Starter Guide PDF.



The updated SEO Starter Guide will replace both the old Starter Guide and the Webmaster Academy. The updated version builds on top of the previously available document, and has additional sections on the need for search engine optimization, adding structured data markup and building mobile-friendly websites.
This new Guide is available in nine languages (English, German, Spanish, French, Italian, Japanese, Portuguese, Russian and Turkish) starting today, and we’ll be adding sixteen more languages very soon.

Go check out the new SEO Starter Guide, and let us know what you think about it.

For any questions, feel free to drop by our Webmaster Help Forums!

Posted by Abhas Tripathi, Search Quality Strategist

Categories: Web Design

The New Smashing Mystery Riddle, An Emoji Edition

Smashing Magazine - Tue, 12/12/2017 - 06:40
Oh yes, the infamous mystery riddles are back! To celebrate the relaunch of this little website, we've prepared something special yet again — a Smashing Emoji Mystery Riddle. And this time, instead of scouting an answer in a physical place or on Twitter, it's well hidden somewhere on this website. So, What Can You Win? Among the first readers who tweet @smashingmag all the hidden emoji, we'll raffle a quite extraordinary, smashing prize (and a couple of other Smashing extras):
Categories: Web Design

Understanding CSS Layout And The Block Formatting Context

Smashing Magazine - Mon, 12/11/2017 - 06:14
There are a few concepts in CSS layout that can really enhance your CSS game once you understand them. This article is about the Block Formatting Context (BFC). You may never have heard of this term, but if you have ever made a layout with CSS, you probably know what it is. Understanding what a BFC is, why it works, and how to create one is useful and can help you to understand how layout works in CSS.
Categories: Web Design

Manipulating HTML5 Canvas Using Konva: Part 1, Getting Started

Tuts+ Code - Web Development - Mon, 12/11/2017 - 05:00

The HTML5 canvas has been supported by all popular browsers for a long time now. You can use it for drawing graphics in a browser with the help of JavaScript. The graphics that can be created on a canvas range from simple lines and shapes to photo compositions and animations.

In this series, you will learn about a very helpful canvas library called Konva. You can use Konva to draw different shapes on the canvas or the stage. Among other things, the library allows you to scale, rotate and animate all these shapes and attach event listeners to them. 

This tutorial will focus on the fundamental concepts of the library while providing a brief overview of different features of the library. After that, we will move on to more specific and complex topics later. The library first came into existence as a fork of the popular KineticJS library. 

Stage, Layers, and Shapes

Everything that you draw with Konva will require you to create a stage using Konva.Stage. You can specify the container element of a stage using the container attribute. Each stage also requires a width and height value which can be specified using the width and height attributes respectively.

A single stage can contain multiple layers. Each of these layers will have two <canvas> renderers. The scene renderer is used to draw all the shapes that are visible to you on the stage. The hit graph renderer is hidden from the users. It is used to perform high-performance event detection.

A single layer can contain multiple shapes, groups of different shapes, or a group of groups. The stage, layers, groups and shapes act like virtual nodes which can be individually styled and transformed.

Drawing Shapes Using Konva

Before we create any shapes, we need to include the library in your project. There are multiple ways of doing this. If you are using package managers, you can run the following commands.

npm install konva // OR bower install konva

You can also link directly to a minified version of the library hosted on cdnjs and jsDelivr.

Once you have installed the library, you can use the following code to create rectangular shapes on the canvas.

var canvasWidth = 600; var canvasHeight = 400; var stage = new Konva.Stage({ container: "example", width: canvasWidth, height: canvasHeight }); var layerA = new Konva.Layer(); var rectA = new Konva.Rect({ x: 75, y: 150, width: 200, height: 50, rotation: 45, fill: "blue", stroke: "black", strokeWidth: 4 }); var rectB = new Konva.Rect({ x: 350, y: 50, width: 100, height: 250, cornerRadius: 50, fill: "red", stroke: "brown", strokeWidth: 10 }); layerA.add(rectA); layerA.add(rectB); stage.add(layerA);

Drawing anything on the canvas is a five-step process. First, you need to instantiate a stage where different shapes would be drawn using Konva.Stage. This requires you to specify the width and height of the stage as well as the id for the container element which would contain the stage. In our case, the rectangles are being drawn inside a div whose id is example.

In the next step, you have to instantiate all the layers that you want to render on your stage. We are only creating a single layer in this example, but you can create multiple layers and add them all to a single stage. Different layers can be very useful when your background consists of static as well as moving elements. In such cases, you can add the static elements on one layer and moving elements on the other. Since you won't have to update the static background after each redraw, this can drastically improve the performance.

After creating the layers, you can initialize different shapes like rectangles, ellipses, stars and rings that you want to show on the layers. Finally, you have to add the shapes to the layers and the layers to the stage.

Creating Groups in Konva

Grouping different shapes together is a good idea when you want to manage all the shapes as a single unit. For example, let's say that you have created a car using two circles for wheels and two rectangular blocks for the body. If you want to move this car ahead without creating a group, you will have to individually translate all the shapes one at a time. A more efficient method is to just add the circles and the rectangles to a group and translate them all at once.

To add any shape to a group, you need to use the add() method, just like you did when adding shapes to a layer. You can also add a group to another group to create more complex entities. For example, you could create a person inside the car as one group and add that person to the group that represents the car.

In the following example, I have kept things simple and only created a carA group. After that, I can rotate and scale the whole car at once.

var canvasWidth = 600; var canvasHeight = 400; var stage = new Konva.Stage({ container: "example", width: canvasWidth, height: canvasHeight }); var layerA = new Konva.Layer(); var wheelA = new Konva.Ring({ x: 130, y: 230, innerRadius: 5, outerRadius: 30, fill: "gray", stroke: "black", name: "First Wheel" }); var wheelB = new Konva.Ring({ x: 270, y: 230, innerRadius: 5, outerRadius: 30, fill: "gray", stroke: "black", name: "Second Wheel" }); var frameA = new Konva.Rect({ x: 80, y: 150, width: 240, height: 60, cornerRadius: 10, fill: "red", stroke: "black", name: "Bottom Frame" }); var frameB = new Konva.Rect({ x: 135, y: 90, width: 120, height: 60, cornerRadius: 10, fill: "orange", stroke: "black", name: "Top Frame" }); var carA = new Konva.Group({ x: 50, y: 0, rotation: 20, scaleX: 1.2 }); carA.add(wheelA, wheelB, frameA, frameB); layerA.add(carA); stage.add(layerA);

Layering in Konva

You already know about layers in Konva. Layering is something different. By default, all shapes that you add to a layer are drawn in the order in which they were added. This means that shapes added to a layer after all others will be drawn on top of other shapes.

Konva allows you to control the order in which the shapes are drawn using different layering methods like moveToTop(), moveToBottom(), moveUp(), moveDown(), and zIndex().

The moveToTop() method will draw the given shape over all other shapes that have been added to the same layer. Shapes drawn on a layer that was added to the Konva stage after the layer of our specific shape will still remain above our shape. This is why the indigo circle in the following example remains below the light green circle even after calling moveToTop().

The moveToBottom() method will draw the given shape below all other shapes that have been added to the same layer. Just like moveToTop(), the shapes will move to the bottom of their own layers and not the layers below them.

The moveUp() and moveDown() methods move the shape on which they are called, one position above or below their current position in the same layer. The zIndex() method is used to set the index of a shape inside its parent layer. Unlike CSS, you cannot set an arbitrary zIndex value in Konva. For a layer with 10 shapes, the zIndex value can only be between 0 and 9 (inclusive).

In the above example, you can see that just pressing the "Indigo Top" button does not draw the indigo circle above all others, while pressing "Indigo Above all Others" will position it at the top. This is because the last button also moves the layer containing the indigo circle to the top.

Since the circles can be dragged around, it might be a good idea to drag them over each other and see how the position of the indigo circle changes when you press different buttons.

Final Thoughts

We have covered some fundamental concepts related to Konva in this tutorial. You should now be able to draw some common shapes on the canvas and move them around as a group. The tutorial also showed you how to push a particular shape up or down in case of an overlap.

JavaScript has become one of the de facto languages of working on the web. It’s not without its learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available on Envato Market.

If you have any questions related to this tutorial, feel free to let me know in the comments. The next tutorial will teach you how to draw basic shapes in Konva.

Categories: Web Design

How to Choose an Ecommerce Platform Based on Business Size

The eCommerce industry has been steadily growing over the past 10 years. In fact, online sales jumped nearly 15% across the board in 2016, and are predicted to continuously...

The post How to Choose an Ecommerce Platform Based on Business Size appeared first on Onextrapixel.

Categories: Web Design

#NoHacked 3.0: How do I know if my site is hacked?

Google Webmaster Central Blog - Fri, 12/08/2017 - 10:54
Last week #NoHacked is back on our G+ and Twitter channels! #NoHacked is our social campaign which aims to bring awareness about hacking attacks and offer tips on how to keep your sites safe from hackers. This time we would like to start sharing content from #NoHacked campaign on this blog in your local language!

Why do sites get hacked? Hackers havedifferent motives for compromising a website, and hack attacks can be very different, so they are not always easily detected. Here are some tips which will help you in detecting hacked sites!

  • Getting started:

    Start with our guide "How do I know if my site is hacked?" if you've received a security alert from Google or another party. This guide will walk you through basic steps to check for any signs of compromises on your site.

  • Understand the alert on Google Search:

    At Google, we have different processes to deal with hacking scenarios. Scanning tools will often detect malware, but they can miss some spamming hacks. A clean verdict from Safe Browsing does not mean that you haven't been hacked to distribute spam.

    • If you ever see "This site may be hacked", your site may have been hacked to display spam. Essentially, your site has been hijacked to serve some free advertising.
    • If you see"This site may harm your computer" beneath the site URL then we think the site you're about to visit might allow programs to install malicious software on your computer.
    • If you see a big red screen before your site, that can mean a variety of things:
      • If you see "The site ahead contains malware", Google has detected that your site distributes malware.
      • If you see "The site ahead contains harmful programs", then the site has been flagged for distributing unwanted software.
      • "Deceptive site ahead" warnings indicate that your site may be serving phishing or social engineering. Your site could have been hacked to do any of these things.
  • Malvertising vs Hack:

    Malvertising happens when your site loads a bad ad. It may make it seem as though your site has been hacked, perhaps by redirecting your visitors, but in fact is just an ad behaving badly.

  • Open redirects: check if your site is enabling open redirects

    Hackers might want to take advantage of a good site to mask their URLs. One way they do this is by using open redirects, which allow them to use your site to redirect users to any URL of their choice. You can read more here!

  • Mobile check: make sure to view your site from a mobile browser in incognito mode. Check for bad mobile ad networks.

    Sometimes bad content like ads or other third-party elements unknowingly redirect mobile users. This behavior can easily escape detection because it's only visible from certain browsers. Be sure to check that the mobile and desktop versions of your site show the same content.

  • Use Search Console and get message:

    Search Console is a tool that Google uses to communicate with you about your website. It also includes many other tools that can help you improve and manage your website. Make sure you have your site verified in Search Console even if you aren't a primary developer on your site. The alerts and messages in Search Console will let you know if Google has detected any critical errors on your site.

If you're still unable to find any signs of a hack, ask a security expert or post on our Webmaster Help Forums for a second look.

The #NoHacked campaign will run for the next 3 weeks. Follow us on our G+ and Twitter channels or look out for the content in this blog as we will be posting summary for each week right here at the beginning of each week! Stay safe meanwhile!

.blgimg img { width: 100%; border: 0; margin: 0; padding: 10px 0 0 0; }
Categories: Web Design

How To Iterate Your Way To A Winning Content-Driven Website

Smashing Magazine - Fri, 12/08/2017 - 06:35
If, like me, you spend most of your days working on content-driven websites, you can feel left out of the cool kid's party. Best practice like Agile, continual iteration, and user feedback don’t sit quite as well when serving up lots of information, rather than a killer web app. When I talk about a content-driven site, I am referring to any website whose primary aim is to convey information, rather than complete tasks.
Categories: Web Design

10 Must-See Easy Digital Downloads Extensions for Your WordPress Site

Tuts+ Code - Web Development - Fri, 12/08/2017 - 04:09

Selling products online has been a game-changer over the past several years. With the reasonable cost of an e-commerce platform, business owners, artists, musicians, and more have started their own online stores.

The power and flexibility of WordPress have been amazing and have led to the rise of the most popular e-commerce solution on the web—WooCommerce.

Because WooCommerce has a broader focus that includes physical goods and services, it has left room for a more efficient and niche solution for e-commerce businesses interested in exclusively selling digital downloads. And that solution is Easy Digital Downloads.

This flexible and easy-to-use free e-commerce platform focuses on digital downloads, providing many useful features that are essential to managing an online storefront. There are, however, many different ways to extend the platform. Let's take a look at 10 must-see Easy Digital Downloads extensions for your WordPress site from Envato Market:

1. Easy Digital Downloads - Points and Rewards

Transform your faithful customers into frequent customers with Easy Digital Downloads - Points and Rewards. Configure and tweak your points program for maximum sales with the many different options included within this Easy Digital Downloads extension for your WordPress site.

"Customers can build up their point balance over time and then choose exactly when they wish to redeem those points for a discount. Customer point balances can be manually adjusted at any time by store admins."

Some of the features include:

  • set maximum discounts based on category, cart, and product levels
  • log generated of all points changes made for customers
  • define point earnings and point values for set discounts
  • and more!

Easily start your customer rewards program with the Easy Digital Downloads - Points and Rewards plugin, today!

2. QuickBooks (Intuit) Gateway for Easy Digital Downloads

As a small business, you may already be using QuickBooks. And if you're already using QuickBooks, you may already have a QuickBooks (Intuit) Merchant Account. If this is your business or could be your business, you need to check out the QuickBooks (Intuit) Gateway for Easy Digital Downloads.

Simply put:

"QuickBooks (Intuit) Payment Gateway provides an easier, cost effective and simple alternative for a small business for accepting credit cards."

QuickBooks (Intuit) Gateway for Easy Digital Downloads. Check it out.

3. Easy Digital Downloads Email Templates

Every touch point you have with a customer is important, especially immediately after they've made a purchase. Set yourself apart from the competition with amazing transactional customer emails from the Easy Digital Downloads Email Templates extension.

"Fully customizable texts, colors & images for your unique store design."

This Easy Digital Downloads extension boasts:

  • responsive design for all screen sizes and mobile devices
  • 20 different styles of email templates
  • customize images, text, colors, fonts, etc.
  • and more!

Add a personal touch or keep your e-commerce storefront branding with every notification and email sent via Easy Digital Downloads.

Easy Digital Downloads Email Templates makes it easy to do, so you can focus on your business.

4. Easy Digital Downloads - Currency Converter

If you're selling something that can be digitally downloaded, then the entire world becomes your marketplace!

So what about other currencies? Easy Digital Downloads - Currency Converter has got you covered.

"Easy Digital Download Currency Converter uses Open Exchange Rates for converting your product prices to a currency of your customers choice!"

Packed with useful features, this is what your world-wide-web store needs:

  • automatically detect currency based on location
  • fully responsive for all screen sizes
  • option for product price appending
  • full localization support
  • and more!

The Easy Digital Downloads - Currency Converter extension takes the pain out of currency conversion.

5. Easy Digital Downloads Product Audio Sampler Player

If you're selling music downloads—or any audio download for that matter—then the Easy Digital Downloads Product Audio Sampler Player is the perfect addition to your Easy Digital Downloads storefront.

Easy to use, install, and configure, this extension lets you add an amazing feature to your e-commerce website.

"You simply upload a sample file in the backend and EDD Player will take care of the rest."

Features include:

  • single or playlist mode
  • 3 custom positions
  • clean design
  • and more

It's a feature that's easy to overlook, but impossible to live without.

The Easy Digital Downloads Product Audio Sampler Player is the perfect extension for music artists and musicians selling through Easy Digital Downloads.

6. Easy Digital Downloads - PDF Vouchers

This is a really great solution for both exclusively online businesses and those that cross over to brick and mortar. Easy Digital Downloads - PDF Vouchers makes it super easy to generate sales and gift vouchers for customers.

"This is perfect for stores that wish to sell tickets to events, discount codes for on-site pickup, class admission tickets and more."

Customers are given the ability to set a recipient name, email, and personal message for vouchers purchased through this Easy Digital Downloads extension for your WordPress site.

It's packed full of features:

  • create voucher templates with a drag-and-drop interface
  • PDF vouchers are fully customizable
  • unlimited or limited codes
  • import codes via CSV
  • and much more!

A great solution for online payments for any types of goods or services. This plugin has enough features to be very useful, without trapping itself into too tight of a niche.

Easy Digital Downloads - PDF Vouchers is one of the more impressive extensions for both local and online businesses.

7. Easy Digital Downloads Product Upsells

Boost your sales with upsells! Not only is it a profitable thing to do, but it can also be very helpful to your customers. Use Easy Digital Downloads Product Upsells to make upsells—easy!

"It will help you to display products you want as an upsell to your customers based on your selection."

Customize the look of product upsells and more:

  • easy to customize
  • easy to setup
  • easy to upsell

Easy Digital Downloads Product Upsells is a simple plugin with a low investment threshold. There's hardly any reason why you shouldn't try this plugin.

8. Easy Digital Downloads - Social Login

Signing up for a new account can be a real hassle. Customers may even add products to their cart, but get bogged down when they are faced with all the required fields to set up a new account.

The first purchase is always the hardest. Unless you're using the Easy Digital Downloads - Social Login plugin.

"...Allows users to log in and checkout with social networks such as Facebook, Twitter, Google, Yahoo, LinkedIn, Foursquare, Windows Live, VK.com, Instagram, Amazon and Paypal."

Features also included:

  • shortcode support to place login wherever you like
  • sends new account details to user email
  • custom URL redirect after social login
  • and much, much more!

The most important feature? One-click registration.

It's never been easier to sign up new customers than with the Easy Digital Downloads - Social Login. You're welcome.

9. Easy Digital Downloads - Dropbox

There are several reasons why it works well to host your e-commerce download files on Dropbox, and Easy Digital Downloads - Dropbox makes it super easy to integrate Dropbox into Easy Digital Downloads.

"Easily choose a file from your Dropbox account via the Choose from Dropbox button and the plugin will do the rest."

This plugin:

  • lets you select and upload files into your Dropbox account from the WordPress Admin
  • is easy to customize, set up, and use
  • is translation ready
  • and more!

Adding Dropbox to Easy Digital Downloads has never been this easy. Easy Digital Downloads - Dropbox is a no-brainer.

10. Fancy Popup - Popup Plugin For WordPress

Fancy Popup - Popup Plugin For WordPress is not specifically built for Easy Digital Downloads—but it is compatible and extremely useful for e-commerce WordPress websites.

"...Design amazing popup(s) with the page builder, and launch popup(s) on any page you want."

The number of pop-up features and options will not disappoint:

  • trigger modes
  • popup positions
  • launching management
  • popup import and export
  • and much, much more

As popup WordPress plugins go, Fancy Popup - Popup Plugin For WordPress is an exceptional plugin. The fact that it's compatible with Easy Digital Downloads and extremely useful for e-commerce just adds to its usefulness.

Conclusion

As someone who has used Easy Digital Downloads extensively, I can tell you that it is a solid platform that has been built with great care. From the moment you install the plugin, you quickly realize that this is a digital-download-focused e-commerce solution.

With time, I am sure we will see more and more useful Easy Digital Download extensions. There are also many Easy Digital Downloads WordPress themes already available, giving users the power to craft and create their own unique digital download e-commerce store.

Have you used Easy Digital Downloads before?

Categories: Web Design

Pages