Wednesday, October 18, 2017

Modal Dialog Box in Angular

In my earlier post I showed how to write an incremental search in Angular. I'm going to extend that walkthrough by adding the ability to add more names to the list using a modal dialog box. We are going to use Angular's Material library. Before we can do this, we need to import a lot more scripts into the SearchPeople.html page. For some reason, the Angular demo pages don't feel the need to tell you this. Here they are...

<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script> 
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>

We also need to add ngMaterial to the module and controller definitions so they look like this.

angular.module("SearchPeopleApp", ['ngSanitize', 'ngMaterial'])
.controller("SearchPeopleCtrl", function ($scope, $mdDialog) {

Next we will add the button that pops up the dialog. After the label that contains the word "Search", add this button.

<a class="btn btn-info" ng-click="addPerson($event)">Add Person</a>

Add the addPerson function after the line $scope.Search = ""

$scope.addPerson = function (ev) {
    $mdDialog.show(
        {
            controller: DialogController,
            templateUrl: 'AddPerson.html',
            parent: angular.element(document.body),
            targetEvent: ev,
            clickOutsideToClose: true
        })
    .then(function (answer) {
        if (answer.First != undefined || answer.Last != undefined)
            $scope.data.push(answer);
    }, function () {
         // Put code here that executes when the user cancels
    });
};

When the user clicks the [Add Person]  button the dialog box is displayed. If the user provides a name we add it to data, which is a list of names. If the user cancels we do nothing.

The function uses a standard dialog box controller which also needs to be defined. Any angular functionality the dialog box needs is added to this controller. The html file referenced by templateUrl above is exactly that - a template. The template gets pulled into the host page which is why the controller is here. It's rather a good way of doing things.

function DialogController($scope, $mdDialog) {
    $scope.hide = function () {
        $mdDialog.hide();
    };

    $scope.cancel = function () {
        $mdDialog.cancel();
    };

    $scope.answer = function (answer) {
        $mdDialog.hide(answer);
    };
}

At this point everything is wired up except for the actual dialog box. Add an html file to the project and call it AddPerson.html.

The important part goes in <md-dialog-content>. The <md-dialog-actions> section contains a single button that, when clicked, creates a small dictionary containing the user's answers and causes it to be passed back to the calling program.

<!DOCTYPE html>
<html>
<head>
    <title></title>
<meta charset="utf-8" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
    <md-dialog>
        <form ng-cloak>
            <md-toolbar>
                <div class="md-toolbar-tools">
                    <h2>Add Person</h2>
                    <span flex></span>
                    <md-button class="md-icon-button md-accent md-raised" ng-click="cancel()">x</md-button>
                </div>
            </md-toolbar>

            <md-dialog-content>
                <div class="md-dialog-content">
                    Enter a new name:
                    <input type="text" ng-model="First"/>
                    <input type="text" ng-model="Last"/>
                </div>
            </md-dialog-content>

            <md-dialog-actions layout="row">
                <md-button ng-click="answer({First:First, Last:Last})">
                    Save
                </md-button>
            </md-dialog-actions>
        </form>
    </md-dialog>
</body>
</html>

The result is quite elegant.



In case you got lost at some point, here is the complete SearchPeople.html.

<!DOCTYPE html>
<html ng-app="SearchPeopleApp">
<head>
    <title>Search People</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-sanitize.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script> 
    <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script>
        angular.module("SearchPeopleApp", ['ngSanitize', 'ngMaterial'])
        .controller("SearchPeopleCtrl", function ($scope, $mdDialog) {
            $scope.data = [{ First: 'Bob', Last: 'Smith' }, { First: 'Mary', Last: 'Jones' }, { First: 'Gary', Last: 'Flowers' }, { First: 'Anne', Last: 'Green' }];
            $scope.Search = "";

            $scope.addPerson = function (ev) {
                $mdDialog.show(
                    {
                        controller: DialogController,
                        templateUrl: 'AddPerson.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true
                    })
                .then(function (answer) {
                    if (answer.First != undefined || answer.Last != undefined)
                        $scope.data.push(answer);
                }, function () {
                });
            };

            function DialogController($scope, $mdDialog) {
                $scope.hide = function () {
                    $mdDialog.hide();
                };

                $scope.cancel = function () {
                    $mdDialog.cancel();
                };

                $scope.answer = function (answer) {
                    $mdDialog.hide(answer);
                };
            }
        })
            .filter("applySearch",
            function () {
                return function (data, Search) {
                    var results = [];
                    if (Search == "") return data;
                    Search = Search.toUpperCase();
                    for (var i = 0; i < data.length; i++) {
                        if (data[i].First.toUpperCase().indexOf(Search) > -1)
                            results.push(data[i]);
                        else if (data[i].Last.toUpperCase().indexOf(Search) > -1)
                            results.push(data[i]);
                    }
                    return results
                }

            })

    </script>
</head>
<body ng-controller="SearchPeopleCtrl">
    <form name="MainForm">
        <div class="well">
            <label>
                Search
                <input type="text" ng-model="Search" />
            </label>
            <a class="btn btn-info" ng-click="addPerson($event)">Add Person</a>
        </div>
        <div class="well">
            <table class="table">
                <thead>
                    <tr>
                        <th>First Name</th>
                        <th>Last Name</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-repeat="item in data | applySearch:Search" ng-form="MainForm">
                        <td>{{item.First}}</td>
                        <td>{{item.Last}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </form>
</body>
</html>

No comments:

Post a Comment