Tuesday, October 31, 2017

Much ado about Prism

The past few days I've been trying to find an online walk through that demonstrates PRISM and I have not been able to find even one that works. Part of the problem seems to be the continuous stream of breaking changes from version to version.

Browse to Amazon.com (in another tab) and search for Microsoft Prism. All the books are for Prism 4 and none of them were written more recently than 2014. We are now on Prism 6 which is incompatible with version 4 so where are the Prism 6 books or even books for version 5? The answer is that authors got fed up with rewriting their books every year. Books are a lot of work to write and publish so if they're going to be obsolete within a year, it's not worth writing them.

You will also see the Prism 4 books on Amazon are dirt cheap. You can't buy a Prism 4 book and expect the examples to work with version 5 or 6 - they won't.Prism 4 is no longer supported so no-one wants Prism 4 books now.

If you ever do get a Prism 5 application to work you can't simply add the version 6 packages and add new code that uses the version 6 features - you have to do a major rewrite.

To be honest, I don't even think Prism adds anything that's worth the trouble, but I'm not sure because absolutely no walk through I can find online actually works.

Time to move onto something else. I don't think Prism is long for this world.

Monday, October 30, 2017

Odd behavior in NavigationService

There appears to be a bug in the Navigation service. When you execute the GoBack method, the frame's Source is updated, but the binding source is not.

Start a new WPF project using C# targeting any framework. Call the project GoBack.


The MainWindow will contain some buttons, some text blocks, and a frame. The buttons allow us to navigate the frame and the text blocks will monitor some properties. The xaml looks like this.

<Window x:Class="GoBack.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GoBack"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <RoutedCommand x:Key="LoadCommand"/>
        <RoutedCommand x:Key="BackCommand"/>
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource LoadCommand}" Executed="Load_Executed"/>
        <CommandBinding Command="{StaticResource BackCommand}" CanExecute="Back_CanExecute" Executed="Back_Executed"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Button Content="Load Page 2" Command="{StaticResource LoadCommand}" CommandParameter="Page2.xaml"/>
            <Button Content="Go Backward" Command="{StaticResource BackCommand}"/>
        </StackPanel>
        <TextBlock Grid.Row="1" Text="{Binding FrameSource, StringFormat='FrameSource={0}'}"/>
        <TextBlock Grid.Row="2" Text="{Binding ElementName=MainFrame, Path=Source, StringFormat ='MainFrame.Source={0}'}"/>
        <Frame Name="MainFrame" Grid.Row="3" Source="{Binding FrameSource}" Background="AliceBlue"/>
    </Grid>
</Window>

The code behind implements the commands and defines a property that is bound to the Frame's Source property.

using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace GoBack
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _FrameSource = "Page1.xaml";
        public string FrameSource
        {
            get { return _FrameSource; }
            set
            {
                if (_FrameSource != value)
                {
                    _FrameSource = value;
                    NotifyPropertyChanged("FrameSource");
                }
            }
        }
        public MainWindow()
        {
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        private void Load_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            FrameSource = e.Parameter.ToString();
        }

        private void Back_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (MainFrame != null && MainFrame.NavigationService != null)
                e.CanExecute = (MainFrame.NavigationService.CanGoBack);
        }

        private void Back_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MainFrame.NavigationService.GoBack();
        }
    }
}

Note we move forward by setting the Frame's Source property via the binding but we navigate back by using the NavigationService's GoBack method.

Add a couple of trivial Page1.xaml and Page2.xaml pages. They have no code behind and the XAML for Page1 looks like this. You can figure out Page2 on your own.

<Page x:Class="GoBack.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:GoBack">
    <Grid>
        <TextBlock Text="Page 1"/>
    </Grid>
</Page>

When you run the program, note the Frame's Source and its bound property both point to Page 1.



Now click [Load Page 2] and note both the Frame's Source and its bound property both point to Page 2. Navigating by setting the bound property keeps them both in sync.



Now click [Go Backward]. Although you move to the correct page, note the bound property is still pointing to Page 2. If you navigate using the NavigationService the bound property does not get updated although the Frame's Source property is updated correctly.



Now change the Frame's XAML definition to explicitly set the binding mode to TwoWay like this.

<Frame Name="MainFrame" Grid.Row="3" Source="{Binding FrameSource, Mode=TwoWay}" Background="AliceBlue"/>

Run the program again and perform the same steps. Now the bound property is updated correctly. Clearly the default Mode for a Frame's Source is OneWay. I have no idea why that would be.



Sunday, October 29, 2017

Frames within frames

In WPF the Frame class contains a navigator class that tracks past history like a web browser. I recently built a page that is contained in a frame and has it's own frame. The navigator was not behaving as I expected it to. As I navigated within the inner frame, the outer frame was handling the navigating. I browsed the Microsoft documentation and found the reason in the Note at the bottom of this page.

Here's a clear(ish) example. It's written in C# with the viewmodels in the code behind so the demo can better focus on the issue.

Start a new WPF project in C# and call it FrameInFrame.


MainWindow simply contains a frame with its content Uri set to MainPage.xaml. It has no code behind.

<Window x:Class="FrameInFrame.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:FrameInFrame"
        Title="Main Window">
    <Frame Source="MainPage.xaml"/>
</Window>

Add a new WPF page called MainPage. This will contain a button and another frame. We will initially load the frame from one page, and when the user clicks the button we will load it from a different page. The XAML looks like this.

<Page x:Class="FrameInFrame.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:FrameInFrame"
      DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Page.Resources>
        <RoutedCommand x:Key="LoadCommand"/>
    </Page.Resources>
    <Page.CommandBindings>
        <CommandBinding Command="{StaticResource LoadCommand}" Executed="LoadCommand_Executed"/>
    </Page.CommandBindings>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Content="Load Subpage 2" Command="{StaticResource LoadCommand}"/>
        <Frame Grid.Column="1" Source="{Binding FrameSource}"/>
    </Grid>
</Page>

The code behind implements INotifyPropertyChanged, provides some properties and handles the command.

using System;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;

namespace FrameInFrame
{
    public partial class MainPage : Page, INotifyPropertyChanged
    {
        private string _FrameSource = "SubPage1.xaml";

        public string FrameSource
        {
            get { return _FrameSource; }
            set
            {
                if (_FrameSource != value)
                {
                    _FrameSource = value;
                    NotifyPropertyChanged("FrameSource");
                }
            }
        }

        public MainPage()
        {
            InitializeComponent();
        }

        private void LoadCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            FrameSource = "SubPage2.xaml";
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
}

Lastly add a page called SubPage1 and one called SubPage2. They don't have any code behind. The XAML for SubPage1 is below. I think you can guess what the XAML for SubPage2 looks like.

<Page x:Class="FrameInFrame.SubPage1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:FrameInFrame">
    <TextBlock Text="Sub Page 1"/>
</Page>

Now run the project and click the button. SubPage2 is loaded and the text changes to reflect this. The frame defined in MainWindow now shows navigation buttons. Even though the MainPage frame did the navigation, the MainWindow frame is handling the navigation. This is the default behavior.


Now change the MainPage frame to look like this, and run the program again.

<Frame Grid.Column="1" Source="{Binding FrameSource}" JournalOwnership="OwnsJournal"/>

Now the inner frame uses it's own navigation service and you can navigate the inner frame without affecting the outer frame's navigation service.

Friday, October 27, 2017

WPF Tab Control with dynamic content load in MVVM

This is written in Visual Basic with the view model in the code behind. Yes, I have to go to confession next Sunday.

This demonstrates how to dynamically load content as different tabs of a tab control are selected, using the MVVM model. The host page has a tab control and a frame. As different tabs are selected, different pages are loaded into the frame area. I did this to simplify and speed up a massive page with six unrelated tabs that has over a thousand properties. It has become too cumbersome, so I looked for a way to split the code up.

Start by creating a Visual Basic WPF project.


Let's start with the content that will be displayed. Add a Page called Page1.xaml and make the content a textblock with the words "This is page 1". The xaml looks like this...

<Page x:Class="Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:PagePerTab"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Page1">
    <Grid>
        <TextBlock Text="This is page 1"/>
    </Grid>
</Page>

Repeat the process for page 2. Your solution now looks like this.



Now open MainWindow.xaml. We will set the DataContext to point to the code behind and define a tab control and a frame.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PagePerTab"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TabControl Grid.Row="0" ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}" Height="25"/>
        <Frame Grid.Row="1" Source="{Binding FrameSource}" NavigationUIVisibility="Hidden"></Frame>
    </Grid>
</Window>

The tab control is bound to a list of strings and the selected item is bound to a string property. The height is set to 25 to collapse the content area. If you know a better way of doing this, drop me a comment.

The rest of the window contains the frame that hosts the desired page. I have turned off the navigation buttons and bound the FrameSource to a URI property.

The code behind looks like this. Most of it is simply defining the properties that are used by the XAML. The only really interesting line of code is the one that sets FrameSource.

Imports System.ComponentModel

Class MainWindow
    Implements INotifyPropertyChanged

    Private _Tabs As New List(Of String) From {"Page1", "Page2"}
    Private _SelectedTab As String
    Private _FrameSource As Uri

    Public Property Tabs As List(Of String)
        Get
            Return _Tabs
        End Get
        Set(value As List(Of String))
            _Tabs = value
            NotifyPropertyChanged("Tabs")
        End Set
    End Property

    Public Property SelectedTab As String
        Get
            Return _SelectedTab
        End Get
        Set(value As String)
            _SelectedTab = value
            NotifyPropertyChanged("SelectedTab")
            FrameSource = New Uri(SelectedTab & ".xaml", UriKind.Relative)
        End Set
    End Property

    Public Property FrameSource As Uri
        Get
            Return _FrameSource
        End Get
        Set(value As Uri)
            _FrameSource = value
            NotifyPropertyChanged("FrameSource")
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Public Sub NotifyPropertyChanged(PropertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
    End Sub

End Class

Give it a try.




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>

Incremental Search in Angular

In an earlier post I showed how to implement an Incremental Search in WPF using MVVM. I recently decided I needed an Incremental Search in an Angular web page. It was trivial, but here it is anyway.

Start a new Web Application called IncrementalSearchAngular. You can use VB or C# and target any framework from 4.0 onward. Select the Empty template.


Add an HTML page called SearchPeople and make it the default page. Our first step is to create a simple page that lists some names. Make SearchPeople.html look like this.

<!DOCTYPE html>
<html ng-app="SearchPeopleApp">
<head>
    <title>Search People</title>
<meta charset="utf-8" />
    <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>
    <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'])
        .controller("SearchPeopleCtrl", function ($scope) {
            $scope.data = [{ First: 'Bob', Last: 'Smith' }, { First: 'Mary', Last: 'Jones' }, { First: 'Gary', Last: 'Flowers' }, { First: 'Anne', Last: 'Green' }];
        })
    </script>
</head>
<body ng-controller="SearchPeopleCtrl">
    <form name="MainForm">
        <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" ng-form="MainForm">
                        <td>{{item.First}}</td>
                        <td>{{item.Last}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </form>
</body>
</html>

You can run the application now and you will see the names listed.
Now let's add a search field to the UI. Immediately below the form tag, insert the following HTML.

<div class="well">
    <label>
        Search
        <input type="text" ng-model="Search"/>
    </label>
</div>

This will populate the $scope variable called Search with whatever the user enters into the textbox. Let's add that $scope variable now. Under the line that starts "$scope.data", add this line.

$scope.Search = "";

Now we need to write the filter that will only return names that contain the search term. Above the last <SCRIPT/> line, insert the following filter definition. This filter receives a list of names and the search term. Any names that contain the search term (case insensitive) are appended to a new list. When it is done, the filter returns the new list. Filters can be daisy chained together, so don't assume the list of names passed to the filter is the same list that was defined in $scope, or even has the same format.

.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
    }
})

Your <HEAD> section should now look like this.

<head>
    <title>Search People</title>
    <meta charset="utf-8" />
    <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>
    <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'])
        .controller("SearchPeopleCtrl", function ($scope) {
            $scope.data = [{ First: 'Bob', Last: 'Smith' }, { First: 'Mary', Last: 'Jones' }, { First: 'Gary', Last: 'Flowers' }, { First: 'Anne', Last: 'Green' }];
            $scope.Search = "";
        })
            .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>

Now you have to use the new filter in the ng-repeat clause. Change the ng-repeat to look like this.

ng-repeat="item in data | applySearch:Search"

Angular is smart enough to know that whenever $scope.Search changes, the applySearch filter needs to be reevaluated. We don't have to explicitly wire up any event handlers.

Implementing an incremental search in Angular has only four simple steps.
1. Create the backing store ($scope.Search)
2. Bind a textbox to the backing store
3. Write the filter (applySearch)
4. Alter the ng-repeat to apply the new filter.