Skip to main content
Engineering LibreTexts

6.1: CRUD Interface

  • Page ID
    27566
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)

    ( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\id}{\mathrm{id}}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\kernel}{\mathrm{null}\,}\)

    \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\)

    \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\)

    \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)

    CRUD is an acronym for the most basic type of user interface. Most interfaces use this pattern, though it might be hard to see sometimes. Consider an online store that has an interface for purchasing an item. The interface allows users to:

    1. create a record, for example to purchase an item from an online store,
    2. read the record, or get the details of the purchase
    3. update, or change the details of the purchase
    4. delete the purchase completely.

    The store then continues the pattern, allowing a user to create a record for a purchase order, consisting of many items to be purchased. This purchase order interface again has a CRUD interface, though purchase order CRUD interface manipulates purchases of individual items.

    The example in this chapter is much simpler than the example application for the store. It will simply create a set of map records that are stored in an array. The CRUD application will add/update/delete the records from the array. The application will use a version of the map application that the books has been developing.

    This map application will use OOP to collect and stored is as an object. The object will be implemented as shown in Chapter 5.6.1.

    Each of the CRUD operations will be implemented using events, which in this book will be called Event Based Programming (EBP). EBP will seem very different and strange to readers familiar with only Procedural Programming, where all program actions emanate from a main method. In EBP, all functions are independent and are run as a result of an event occurring.

    To help in understanding the system design and implementation, this chapter will be structured as follows:

    1. An initial design of the system (a mockup of how the system should generally look and act) will be created.
    2. The HTML and CSS programs for this mockup will be implemented, separately from the functionality that will be included in the system. The design look-and-feel of the system will be kept separate from the functionality throughout the implementation, and in the real world represent two completely different skill sets.
    3. The forms in the application will be mined for the data needed for the application. The arrays and objects needed to implement this design will be created.
    4. The events needed to run the application will be defined, and a brief description of how to implement each event will be given.
    5. The code to handle each event will be written.

    At the end of these steps, a fully functioning CRUD interface will have been developed.

    6.1.1 Overall Application Design

    The first step in designing a system is to create some sort of mockup of the system to see how it will work. This type of mock up is called a wire frame. The purpose of a wire frame is to set up what the system will look like to define the options that will be available in the system, as well as the data and any data dependencies. The following is a wire frame design of the application which was generated in Pencil.

    Figure 17 Wire Frame design for CRUD interface in Pencil

    Screen Shot 2020-07-04 at 7.23.36 PM.png

    The application will consist of two panels. Panel 1 will always be displayed while the application is running. Panel 1 contains a list box which shows the map features in an array of map records managed in this application. The map records in this application will be a global variable named records and should be the only global variable in the program.

    Also in Panel 1 are several buttons to define user operations. These operations will generate events which will need to be handled. Details on how to handle the events will be given in a later section, but the following is a general overview of the functionality on Panel 1.

    Associated with the list box of map records are the buttons Create, Read, Update, and Delete. These buttons will read the current record highlighted in the list box and run the appropriate operation on that map object.

    There are a second set of buttons which are program options, they are Save to File, and Read from File. The save to file button will write the array to some persistent store using a JSON format. The read from file will read a persistent store previously written and parse the JSON to restore a semantically identical copy of the data previously stored in the program.

    Panel 2 is a form to manipulate data about the features. Panel 2 should be hidden unless the user has selected the create, read, or update options. If the user is not actively interacting with a specific feature, Panel 2 should not be displayed. When Panel 2 is shown, fields that cannot be written to should be read only. Proper editing of the fields for correct values should be done. The values of the edits and read only setting will be specified in the data section of this chapter.

    On Panel 2 there are two buttons. The first is a cancel button, which throws away any edits that have been performed on the data on this panel. The second is the save button, which saves changes made to this form. Note that the save button will be displayed for both the create and update options, though the behavior will be different between those two options.

    6.1.2 Creating the CSS and HTML definition

    The CSS and HTML files for this application are largely defined in previous chapters of this text, and so are presented here without comment.

    Program 106 – CSS for CRUD interface
    
    /*
     *    File:    CRUD.css
     *    Author:  Charles W. Kann
     *    Date:    July 8, 2017
     *
     *    Purpose: To define the styling for the CRUD application
     */
    
    /*
        CSS for the header of the page
     */
    header {
        margin : 5px 50px 5px 50px;
        border : 2px solid blue;
        background-color : slategray;
        color : white; 
    }
    
    header p {
        font-size : 150%;
    }
    
    .header-icon {
        display : inline-block;
        margin: 50px 10px 50px
    }
    
    .header-desc {
        display : inline-block;
        margin : 25px;
    }
    
    .header-menu {
        display : inline-block;
        float: right;
        margin : 50px 50px 50px 50px;
    }
    
    /*
        CSS for the input form (Panel 2)
     */
    #inputForm {
        margin : 0px 50px 5px 5px;
        background-color : beige;
        border : 2px solid black;
        display: none;
        float : right;
        padding : 20px;
        width : 42%;
        height : 70%;
    }
    
    /*
        CSS for the records (Panel 1)
     */
    #recordDivision {
        margin : 0px 5px 5px 50px;
        background-color : beige;
        border : 2px solid black;
        display: inline-block;
        float : left;
        padding : 20px;
        width : 35%;
        height : 70%;
    }
    
    /*
        Select list for records
     */
    #dataRecords {
        width : 300px;
    }
    
    /*
        Gray out read-only input
     */
    input:-moz-read-only { /* For Firefox */
        background-color: lightgray;    
    }
    
    input:read-only {
        background-color: lightgray;         
    }             
    
    Program 107 – HTML CRUD interface
    
    <!--
        File Name:  CRUD.html
        Author:     Charles Kann 
        Date:       7/8/2017
        Purpose:    To define the Map Example CRUD application.
        Modification History: 7/8/2017 - Initial Release
    -->
    
    <html>
        <head>
            <meta charset="UTF-8">
            <title>Map Example </title>
            <link rel="stylesheet" type="text/css" href="CRUD.css">
                <script
    src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.Java
    Script">
                </script>
                <script src="Map.JavaScript"> </script>
                <script src="CRUDLibrary.JavaScript"> </script>
                <script src="CRUDOnload.JavaScript"> </script>
                
                <script>
                    // Global records array declaration
                    var records = new Array();
                </script>
        </head>
        
        <body>
            
                <div class="header-icon">    
                    <image src="GRI_logo.png" />
                </div>
                <div class="header-desc">    
                    <h1>Map Example</h1>
                    <p class="header-p">
                        Example map input screen
                    </p>
                </div>
                <div class="header-menu">
                    <p class="header-p">
                        Home File About
                    </p>
                </div>    
            </header>    
    
                <section  >
                    <h1> Data records </h1>
                        
                        <select  size="7" >
                        </select>
                    

    <input type="button" value="Create" /> <input type="button" value="Read " /> <input type="button" value="Update" /> <input type="button" value="Delete" /> </p>

    <input type="button" value="Save To File" id = "saveFile" /> <input type="button" value="Read From File" /> </p> <p > <input type="text" size="50" /> </p> </section> <section />

    <label for="title">Title</label> <input type="text" size="20"> </p>

    Map Options<br> <label for="resize">Allow map to be resized: </label> <input type="checkbox" /> <br/> <label for="recenter"> Allow map to be recentered: </label> <input type="checkbox" checked /> </p>

    Type of Map<br> <input type="radio" name="maptype" value="XYZMap"/> <label for="XYZMap">XYZ map </label> <br/> <input type="radio" name="maptype" value="StamenMap" checked /> <label for="StamenMap">Stamen Map </label> </p>

    Screen Size<br> <input type="radio" name="screenSize" checked value="600x480" /> <label for="XYZMap">600x480 </label> <br/> <input type="radio" name="screenSize" value="1024x768"/> <label for="1024x768">1024x768 </label> <br/> <input type="radio" name="screenSize" value="1280x800"/> <label for="XYZMap">1280x800 </label> </p>

    Center of Map<br> <label for="lat">Latitude </label> <input type="number" value="-77" /><br /> <label for="long" >Longitude </label> <input type="number" value="39" /> </p> <input type="button" value="Save" /> <input type="button" value="Save" /> <input type="button" value="Cancel" /> </section> </body> </html>

    This html results in the following screen.

    Figure 18 HTML implementation of the wire frame design

    Screen Shot 2020-07-04 at 8.02.23 PM.png

    6.1.3 Application Data

    The following diagram shows the data, from the forms, which will be needed for the application.

    Figure 19 Data items found in the design

    Screen Shot 2020-07-04 at 8.03.59 PM.png

    The data in this application consists of the items that make up a map object (all the fields circled in red in Panel 2) and an array of all the map objects (the list box circled in red in Panel 1). The JavaScript definition of the map data fields can be represented as a Map constructor function as shown below.

    This JavaScript data represents a single entity, and so is stored in a separate file.

    Program 108 – Map object definition
    
    /*    
        File name:    MyMap.js
        Type:         Object definitoin
        Purpose:      To create and manipulate Map objects
        Author:       Charles Kann
        Date:         July 7, 2017
         
        Modification History: 7/7/2017 - Initial Release
     */
    function MyMap(options) {
        // Set the Constructor Function Type
        this.__cfName = "MyMap"; 
        // All object must be created with a title...
        if (options.title == null || options.title == undefined)
            throw "Title must be specified";
        
        // Set default properties for a map
        // Note that title is set in case a user
        // creates this object using Object.create;
        this.id = null;
        this.title = "Please change the title"
        this.recenter = true;
        this.resize = false;
        this.mapType = "Stamen";
        this.screenSize = "640x480";
        this.lat = -77;
        this.long = 39;
        
        // Get properties from parameter
        // Note that this acts like operator overloading. If
        // a property is not set in the parameter, the default
        // will be used. Also this allows for other data fields
        // that might have been added.
        outerloop:
            for (i in options) {
                for (j in this) {    
                    if (i == j) {
                        this[i]= options[j];
                        continue outerloop;
                    }
                }
                console.log("Property " + i + " is not a default Map property");
                    this[i] = options[i];
        }
        
        // Return the current object. This is not needed, but
        // but doesn't hurt and makes the intent clear.
        return this;
    }
    
        // Function:  update
        //  Purpose:  to update all of the properties in the object.
        //            Note that this function allows unstructured properties
        //  Input:    objectbject with property values to update
        // Output:    none
        // Side Effects: object properties are updated.
    MyMap.prototype.update = function(options) {
    
        // Title property cannot change on update. It can be
        // thought of as the immutable key for the object.
        var t1 = options.title;
        var t2 = this.title;
        if (t1 != t2)
            throw "Title changed not allowed in update";
            
        // Get properties from parameter
        // Note that this acts like operator overloading. If
        // a property is not set in the parameter, the default
        // will be used. Also this allows for other data fields
        // that might have been added.
    outerloop_1:
        for (i in options) {
            for (j in this) {
            if (i == j) {
                this[j]= options[i];
                continue outerloop_1;
            }
        }
        console.log("Property " + i + " is not a default MyMap property");
        this[i] = options[i];
      }        
    }
    
    // Function:    toString
    // Purpose:     to create a string representation of the object 
    // Input:       none
    // Output:      string representation of the object
    // Side Effects:none
    MyMap.prototype.toString = function() {    
        return (JSON.stringify(this));    
    }
    

    The map objects are stored in a global array variable in the head of the CRUD.html file. This is the only global variable in this program. Global variables should be used sparingly, and only if the intent is known and can be explained. In this case, the records will be used in both the constructor function and the library functions, and thus needs to be global.

    6.1.4 Mapping the object to data fields

    Now that the properties for the object has been defined, the next step is to map it to the form which was generated earlier. This is done in the table below. This table contains 3 columns. The first column is the name of the item in the object. The second column is the corresponding field name on the form.

    The last column requires some explanation. Panel 2 can be brought up in 3 modes that are create, read, or update. For each of these fields the field has 2 possible attributed, if it is read- only, and if it is visible. This column defines the value of these attributes for each. Note that the two buttons on Panel 2 (circled in green) will also have values for each mode of the form, and so these two buttons are included in the table.

    Object Name

    Field Name

    Notes

    Default value

    Attributes

    title

    title

    “Enter Title”

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = true

    recenter

    resize

    false

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = false

    resize

    recenter

    true

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read only = false

    mapType

    mapType

    Values are from
    radio buttons are: Stamen, XYZMap

    Stamen

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = false

    screenSize

    Values are from
    radio buttons are: 600x480, 1024x768, 1280x800

    600x480

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = false

    lat

    lat

    -77

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = false

    long

    long

    39

    Create: Shown, read_only = false

    Read: Shown, read_only = true

    Update: Shown, read_only = false

    Cancel Button

    Create: Shown, behavior: hide form

    Read: Shown, behavior: hide form

    Update: Shown, behavior: hide form

    Save Button

    Create: Shown, function: SaveNew

    Read: Hidden

    Update: Shown, function: SaveUpdate

    This table allows functions to be written that reset the form (sets the values to defaults), reads the values from the form into an object, set the values on the form to values from an object, and to set the attributes for the fields on the form to read-only or read-write. It shows that 2 Save buttons are needed (one for update, and one for create), and how to set the visibility of those two Save buttons.

    The functions defined in this table are shown below and are stored in a file CRUDLibrary.JavaScript. The attributes for the buttons will be set later when the behavior for the events are defined.

    Program 109 – Library functions for the CRUD application
    
    /*    
        File:        CRUDLibrary.js
        Author:      Charles Kann
        Date:        July 8, 2017
        
        Purpose:     To define library functions needed for the Map Example CRUD application
        
        Methods:     resetForm
                     writeDataToForm
                        readDataFromForm
                        setFormReadWrite
                        setFormReadOnly
        
        Modification History: 7/8/2017 - Initial Release
    
     */
     
    /*
        Function:     resetForm
        Author:       Charles Kann
        Date;         7/8/2017
        Purpose:      Set form to default values
        Input:        None
        Output:       None
        Side Effects: Fields on form have default values
     */
    function resetForm() {
        $("#title").val("");
        $("#resize").prop("checked", false);
        $("#recenter").prop("checked", true);
        $("#StamenMap").prop("checked", true);
        $("#600x480").prop("checked", true);
        $("#lat").val("-77"); 
        $("#long").val("39");
    }
    
    /*
        Function:     writeDataToForm
        Author:       Charles Kann
        Date;         7/8/2017
        Purpose:      Set form to values form obj
        Input:        obj - an array containing data values. 
                            obj must have values for all fields.    
        Output:       None
        Side Effects: Fields on form have values from obj
     */
    function writeDataToForm(obj1) {
        $("#title").val(obj1.title);
        $("#resize").prop("checked", obj1.resize);
        $("#recenter").prop("checked", obj1.recenter);
        $("#"+obj1.mapType).prop("checked", true);
        $("#"+obj1.screenSize).prop("checked", true); 
        $("#lat").val(obj1.lat);
        $("#long").val(obj1.long);
    }
    
    /* 
        Function:     readDataFromForm
        Author:       Charles Kann
        Date;         7/8/2017
        Purpose:      create an object with data values for all fields on form
        Input:        None
        Output:       obj - an object with the data values
        Side Effects: None
     */
    function readDataFromForm() {
        obj = new Object();
        obj.title = $("#title").val();
        obj.resize = $("#resize").is(":checked");
        obj.recenter = $("#recenter").is(":checked");
        obj.mapType = $("input[name='maptype']:checked").val();
        obj.screenSize = $("input[name='screenSize']:checked").val();
        obj.lat = $("#lat").val();
        obj.long = $("#long").val();
        
        return obj;
    }
    
    /*
        Function:    setFormReadWrite
        Author:      Charles Kann
        Date;        7/8/2017
        Purpose:     Set all form fields to allow reading and writing.
        
        Input:       None
        Output:      None
        Side Effects: Fields on form are read/write
     */
    function setFormReadWrite() {
        $("#title").attr('readonly', false);
        $("#resize").attr('disabled', false);
        $("#recenter").attr('disabled', false);
        $("#StamenMap").attr('disabled', false);  
        $("#XYZMap").attr('disabled', false);
        $("#600x480").attr('disabled', false); 
        $("#1024x768").attr('disabled', false); 
        $("#1280x800").attr('disabled', false); 
        $("#lat").attr('readonly', false); 
        $("#long").attr('readonly', false);
    }
    
    /*
        Function:    setFormReadOnly
        Author:      Charles Kann
        Date;        7/8/2017
        Purpose:     Set all form fields to read-only
        Input:       None 
        Output:      None   
        Side Effects: Fields on form are readonly
    */
    function setFormReadOnly() {
        $("#title").attr('readonly', true); 
        $("#resize").attr('disabled', true); 
        $("#recenter").attr('disabled', true); 
        $("#StamenMap").attr('disabled', true); 
        $("#XYZMap").attr('disabled', true); 
        $("#600x480").attr('disabled', true); 
        $("#1024x768").attr('disabled', true); 
        $("#1280x800").attr('disabled', true); 
        $("#lat").attr('readonly', true); 
        $("#long").attr('readonly', true);        
    } 
    

    6.1.5 Application behavior – events

    Many readers will have come to this book having had a minimum of programming experience, and often that experience is with a program that begins with a main method from which all actions in the program emanate. The program works in a top-down manner, where all actions are part of tree rooted in the main method.

    The programming model presented here uses a very different model of programming that we will call Event Based Programming (EBP). EBO is very different from procedural. First, there is no main in the program that is the parent of all the actions in the program. Functions are triggered (or execution called) via asynchronous events, in our case as actions from the user. The system then runs code to respond to that event, returning the program to some known, safe state from which it can respond to other events.

    In the application in this chapter, events are generated by the user when a button is pressed. The pressing of the button creates an event that calls a function associated with that button.

    Unlike the design of a procedural program, which proceeds in a top down manner, EBP sets up the framework for the program, and then defines functions for each of the behaviors (or buttons presses) in this application.

    To design the program a table is created to explain the behavior to respond to each button press. The following figure shows the buttons that need to be created for this application. This is translated into a table where each button is assigned an id, and a column is created to outline the behavior for each button.

    Note that one of the buttons in the diagram, the save button in Panel 2, is special in that it will be implemented as two separate buttons. The reason is that the save button is context sensitive; it will call one function when the user is doing a create, and another function when the user is doing an update. This will be implemented as two separate buttons having different ids but the same values. It will appear to the user that the button is a Save button but depending on the context the button will be different.

    There is one other issue. A message box is provided to give feedback to the user. Each button will have zero, one, or more messages that they can provide.

    Figure 20 buttons with functionality to be defined

    Screen Shot 2020-07-04 at 8.54.16 PM.png

    Button

    ID

    Notes

    Behavior

    Create

    create

    1. Set form to default values (resetForm())
    2. Clear and hide message box
    3. Set form so all fields can be edited (setFormReadWrite())
    4. Set buttons:
      1. saveNew show
      2. saveUpdate hide
      3. cancel show
    5. Display input form

    Read

    read

    1. Get the title (key) from list
    2. Read record from records array
      1. Print error and throw exception if not defined.
    3. Clear and hid message box
    4. Write data to forrm (writeDataToForm(obj))
    5. Set form so all fields are read-only (setFromReadOnly())
    6. Set buttons
      1. saveNew hide
      2. saveUpdate hide
      3. cancel show
    7. Display input form

    Update

    update

    1. Get the title (key) from list
    2. Read record from records array
      1. Print error and throw exception if not defined.
    3. Clear and hid message box
    4. Write data to forrm (writeDataToForm(obj))
    5. Set form so all fields are readWrite(setFromReadWrite())
    6. Set the title to read-only
    7. Set buttons
      1. saveNew hide
      2. saveUpdate show
      3. cancel show
    8. Display input form

    Delete

    delete

    1. Get the title (key) from list
    2. Read record from records array
      1. Print error and throw exception if not defined.
    3. Prompt to confirm delete
      1. If no – message that record not deleted
      2. If yes
        1. Remove record from array
        2. Remove item from list
        3. Message that item was deleted

    Save to File

    saveFile

    1. Use JSON.strinigfy to make a JSON formated file for the records array, and LocalStorage.setItem to put it in local storage.

    Read from File

    readFile

    1. Empty the list
    2. Empty the records array
    3. Read and parse the array from local storage
    4. For each member of the arrray
      1. Parse data
      2. Store to list
      3. Store to records array

    Cancel

    cancel

    1. Hide input form

    Save

    saveUpdate

    1. Read data from form (readDataFromForm())
    2. Get object from records array
    3. If record not found, throw exception
    4. Update record (obj.update())
    5. Hide input form.

    Save

    saveNew

    1. Read data from form (readDataFromForm())
    2. Create new map object
    3. Push object on records
    4. Update the list
    5. Hide input form.

    These behaviors are attached to the buttons when the form is loaded, which is done in the JQuery onload function in the file CRUDOnload.JavaScript. This file is shown below.

    Program 110 – Applications events set in the onLoad function
    
    /*
        File Name:     CRUDOnload.js
        Author:        Charles Kann
        Date:          July 9, 2019
        Purpose:       To initialize the application, and set update the event function call backs.
        
        Events in this file:
            create - create a new Map record
            read - read a Map record (display only)
            update - update a Map record
            delete - delete a Map record
            save to file - save the record array to local storage
            read from file - read the record array from local storage
            cancel - cancel editing a Map record
            save(1) - save a map record on create
            save(2) - save a map record on update
        
        Modification History: 7/9/2017 - Initial release
    */
    $(function() {
        // Set form to default values
            resetForm();
            $("#userMessage").hide();
            $("#userMessage").val("");
            
            /*
                Function:  Create a new record
                Purpose:   To respond to the create button
             */
             $("#create").click( () => {
             
                 // Initialize form 
                 $("#userMessage").hide();
                 $("#userMessage").val("");
                 resetForm();
                 setFormReadWrite();
             
                 // Set buttons
                 $("#saveNew").show();
                 $("#saveUpdate").hide();
                 $("#cancelEdit").show();
                 $("#inputForm").show();
            });   
            
            /*
                Function:   Read a Map record
                Purpose:    To respond to the read button
             */
            $("#read").click( () => {      
                // get record to read from list
                var myTitle = $("#dataRecords").val();
                
                //Find the record to read
                var recordToUpdate = records.find( (currentObject) => {
                    return (currentObject.title == myTitle)     
                });
                
                // Record is not found, throw exception
                if (recordToUpdate == undefined) {
                    $("#userMessage").show();
                    $("#userMessage").val("Record does not exist - Make sure a record is selected" );
                        throw "The title does not exists";
                }
                
                // Initialize form
                $("#userMessage").hide();
                $("#userMessage").val("");
                writeDataToForm(recordToUpdate);
                setFormReadOnly();
                
                // Set buttons
                $("#saveNew").hide();
                $("#saveUpdate").hide();
                $("#cancelEdit").show();
                $("#inputForm").show();
            });
            
            /*
                Function:    Update a Map record
                Purpose:     To respond to the update
                button
             */
             $("#update").click( () => {
            // get record to read from list
            var myTitle = $("#dataRecords").val();
            
            //Find the record to read
            var recordToUpdate = records.find( (currentObject) => {
                return (currentObject.title == myTitle)
            });
            
            // Record is not found, throw exception
            if (recordToUpdate == undefined) {
                $("#userMessage").show();
                $("#userMessage").val("Record does not exist - Make sure a record is selected" );
                    throw "The title does not exist";
            }
            
            // Initialize form
            $("#userMessage").hide();
            $("#userMessage").val("");
            writeDataToForm(recordToUpdate);
            setFormReadWrite();
            $("#title").attr('readonly', true);
            
            // Set buttons
            $("#saveNew").hide(); 
            $("#saveUpdate").show(); 
            $("#cancelEdit").show(); 
            $("#inputForm").show();   
        });
        
        /*
            Function:    Delete a Map record
            Purpose:     To respond to the delete button
         */
         $("#delete").click( () => {
             // get record to read from list
             let myTitle = $("#dataRecords").val();
             
             // Record is not found, throw exception
             if (myTitle == null)
             {
                 $("#userMessage").show();
                 $("#userMessage").val("Record does not exist - Make sure a record is selected" );
                 throw "The title does not exist";
             }
             
             // Confirm Delete
             let retVal = confirm("Do you want to delete " + myTitle + " ?");
             if( retVal == true ){
                 //Find the record to read
                 let index = records.findIndex ( (currentObject) => {
                     return (currentObject.title == myTitle)
                 });  
                 
                 // Record is not found
                 if (index == -1) {
                     $("#userMessage").show();
                     $("#userMessage").val("Record " + myTitle + " not found");
                 }      
                 // Record is found, remove it from records and list
                 else {
                     records.splice(index, 1);
                     $("#dataRecords option[value='" + myTitle + "']").remove();
                     $("#userMessage").show();
                     $("#userMessage").val("Record " + myTitle + " deleted");
                 }  
            }    
            
            // Inform the user the record is not deleted, as
                // was indicated by return from dialog
            else{
                $("#userMessage").show();
                $("#userMessage").val("Record " + myTitle + " not deleted");
            }
            });
        /*
            Function:    Save records to local storage
            Purpose:     To respond to the Save To File button
         */
         $("#saveFile").click( () => {
             localStorage.setItem("MapData", JSON.stringify(records));
         });
    
        /*
            Function:    Read records from local storage
            Purpose:     To respond to the Read From File button
         */
         $("#readFile").click( () => {    
         
            // clear out the list and array                     
                $("#dataRecords").empty();     
            records = new Array();
             
            // get the records array from local storage
            let arr = JSON.parse(localStorage.getItem("MapData"));   
            
            // Each record was set to JSON independently. Get
                // each record, parse it, and put it in the records
                // array and list.         
            for (var i = 0; i < arr.length; i++) {
                records[i] = new MyMap(arr[i]);
                $('#dataRecords').append($("<option></option>")
                    .attr("value", records[i].title)    
                    .text(records[i].title));
            }
        });
        
        /*
            Function:    Save record data from create
            Purpose:     To respond to the Save Button
         */
          $("#saveNew").click( ()=> {
          
            // See if record exists. Do not allow duplicates.
            var saveObj = readDataFromForm();
            var recordExists = records.find( (currentObject, idx) => {    
                return (currentObject.title == saveObj.title)
            });
            if (recordExists != undefined)
                throw "The title already exists";
                
                // Put new record in records array, and update list.          
            records.push(new MyMap(saveObj));
            $('#dataRecords').append($("<option></option>")
                             .attr("value",saveObj.title)   
                             .text(saveObj.title));
                             
            // hide the input form
            $("#inputForm").hide();
        });      
    
        /*
            Function:    Save record data from update
            Purpose:     To respond to the Save Button
         */
         $("#saveUpdate").click( () => {          
         
            // Check if record exists. It must exist to
                // update it.
            var saveObj = readDataFromForm();
            var recordToUpdate = records.find(function(currentObject) {
                return (currentObject.title == saveObj.title)
            });
            
            if (recordToUpdate == undefined)
                throw "The title does not exists";
                
                // Update record.
            recordToUpdate.update(saveObj);
            
            // hide the input form.
            $("#inputForm").hide();
        });
        
        /*
            Function:    Cancel Edit
            Purpose:     To respond Cancel Button
         */
          $("#cancelEdit").click( () => {
            $("#inputForm").hide();
        });        
    });                                                                                                                                    
    

    This page titled 6.1: CRUD Interface is shared under a CC BY license and was authored, remixed, and/or curated by Charles W. Kann III.

    • Was this article helpful?