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:
- create a record, for example to purchase an item from an online store,
- read the record, or get the details of the purchase
- update, or change the details of the purchase
- 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:
- An initial design of the system (a mockup of how the system should generally look and act) will be created.
- 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.
- 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.
- The events needed to run the application will be defined, and a brief description of how to implement each event will be given.
- 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.
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.
6.1.3 Application Data
The following diagram shows the data, from the forms, which will be needed for the application.
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 |
Stamen |
Create: Shown, read_only = false Read: Shown, read_only = true Update: Shown, read_only = false |
screenSize |
Values are from |
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.
Button |
ID |
Notes |
Behavior |
Create |
create |
|
|
Read |
read |
|
|
Update |
update |
|
|
Delete |
delete |
|
|
Save to File |
saveFile |
|
|
Read from File |
readFile |
|
|
Cancel |
cancel |
|
|
Save |
saveUpdate |
|
|
Save |
saveNew |
|
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(); }); });