angular validation - prevent button ng-click() when form is invalid

Problem

I have an angular form as part of a single page App. The 'Continue' button moves a user to the next section of the app, and has an ng-click. I would like to prevent this ng-click from happening when the button is clicked and the form is invalid, but I don't want to disable the Continue button.

Example: A user comes to the page and immediately clicks the Continue button without providing a first name. The error message "You should provide a first name" is shown and the user remains on the page.

How can I do this? Here is the code:

<form name="form" novalidate >

<div ng-show="form.FirstName.$touched && form.FirstName.$error.required" class="errorMessage">
    You must enter a first name
</div>

<div class="form-group">
    <label for="firstName" class="form-label">First Name</label>
    <input id="firstName" class="form-control" name="FirstName" ng-model="vm.user.firstName" required />
</div>

<div class="btn-group">
    <button ng-click="vm.next()">Continue</button>
</div>
</form>
Problem courtesy of: noclist

Solution

Just add form.$valid && before the function call:

<form name="form" novalidate >

<div ng-show="form.FirstName.$touched && form.FirstName.$error.required" class="errorMessage">
    You must enter a first name
</div>

<div class="form-group">
    <label for="firstName" class="form-label">First Name</label>
    <input id="firstName" class="form-control" name="FirstName" ng-model="vm.user.firstName" required />
</div>

<div class="btn-group">
    <button ng-click="form.$valid && vm.next()">Continue</button>
</div>
</form>
Solution courtesy of: Carl Ambroselli

Discussion

Either a button is disabled or it's clickable, you can't have it both ways.

What you could do in your controller for vm.next() is say:

myApp.controller('Page1Controller'),  ['$scope', function ($scope)
{
    $scope.vm = {};

    $scope.vm.next = function()
    {
        if (! $scope.vm.isFormValid())            
            return;            
        //move to the next page with your current code
        ...
    }

    $scope.vm.isFormValid()
    {
        $scope.vm.shouldShowError = $scope.form.valid; //add vm.shouldShowError to your error message ng-show in the view so it will display only after clicking continue
        return $scope.form.valid;
    }
}]);
Discussion courtesy of: Richard

This recipe can be found in it's original form on Stack Over Flow.