Problem: Define a structure (or "model") with data fields whose values can be set using AJAX and by completing a HTML form.

Setup

You will need copies of the following files:

Motivation

We already know that object literals can be written as:

var literalObj = {
	someText: "Hello World",
	someNumber: 10
};
JSON also provides data in a similar form, so converting this object to JSON is straightforward, in jQuery it looks like this:
var json = JSON.stringify(literalObj);

The objective is to build a class that contains an object literal and provides methods to read and write data to it. When you create a model using this class, you define the data fields that are associated with the model by passing an object literal in the constructor together with the name of the primary key.

Usage

To use the Model class we begin by specifying the data fields associated with the model and the name of the primary key field. In the following example, the primary key field is "id" and the two data fields are "id" and "name".
var dataFields: Object;
var primaryKeyName: string;
var model: Model;

primaryKeyName = "id";
dataFields = { id: 0, name: "Fred"}

// Pass the data structure to define the data fields 
// the Model should use and the name of primary key field
model = new Model(dataFields, primaryKeyName);
The model should ensure that only the data fields specified this way can be used by providing a SetData() and GetData() that will only set or get values for the data fields specified in the constructor. Extending the previous example, we can only set or get the "id" and "name" values.
var model: Model = new Model({ id: 0, name: "Fred"}, "id");
var id: number;
var name: string;

model.SetData("id", 2);
model.SetData("name", "Fred");

id = model.GetData("id");      ← result is 2
name = model.GetData("name");  ← result is "Fred"
The class will also provide methods for converting the object into HTML pairs or JSON format.

Details

Wrapping an object in a class would look like this:
class Model
{
    // Attributes
    private _data: Object; ← This is the object literal
    
    private _primaryKeyName: string = "id";
    private _primaryKeySet: boolean = false;

    // Constructor
    constructor(dataFields: any, primaryKeyName: string)
    {
        this._data = dataFields; ← Here we store the object
        this._primaryKeyName = primaryKeyName;
    };
Line 4 shows the attribute that will contain the object literal and at line 11 the attribute is set by passing an object literal into the constructor.

When setting the values would like to be able to use a method SetData() that checks that the property name is valid (and in a later exercise we also want to check that the value provided is the correct type). Any attempt to set a property that does not exist should be ignored, or you may choose to return a boolean if you wish.

var myData: Model = new Model();
var propertyName: string = "name";
var name: string =  "Fred";

myData.SetData(propertyName, name);

// Or set value this way
model.SetData("age", 22);
We will also need a method GetData() to read a stored value. Any attempt to use a property that
doesn't exist should result in undefined being returned.
var myData: Model = new Model();
var propertyName: string = "name";
var name: string;

name = myData.GetData(propertyName);
if(name !== undefined) window.alert("Name is " + name);

// Or read value this way
var age: number;
age = myData.GetData("age");

// Attempt to read non-existant field results in undefined being returned
age = myData.GetData("non-existant-field-name");
if(age === undefined) window.alert("Data field does not exist");
The methods should look like this:

SetData(fieldName: string, value: any): boolean
{
	// Your code here
}

GetData(fieldName: string): any
{
	// Your code here
}
Finally, the class must handle the primary key slightly differently. The class will need to told the primary key name and if the key value is not set then the GetData() method must return undefined

.
var myData: Model = new Model("id");
var keyName: string = "id";
var keyValue: number;

// Key not set so undefined returned
keyValue = myData.GetData(keyName);
if(keyValue  === undefined) window.alert("Key value not set yet");

// Set the key value
keyValue = 2;
myData.SetData(keyName, keyValue);

// Key value set, so value is returned
keyValue = myData.GetData(keyName);
if(keyValue  !== undefined) window.alert("Key value is " + keyValue);
HTML 
To convert the data in the wrapped object to name/value pairs try including the HTMLPairsConvertor.ts file and adding the following method to the Model class:
ToHTMLPairs(skipList?: string[]): string
{
	return HTMLPairsConvertor.ConvertObjToPairs(this._data, skipList);
}
The skipList is optional and is used when you wish to prevent properties from being converted to pairs. For now you can ignore this argument when you test this method.

You might use it like this:
var myData: Model = new Model();
var pairs: string;
model.SetData("name", "Fred");
model.SetData("age", 22);

pairs = model.ToHTMLPairs(); ← name=Fred&age=22

Hints

  1. You can use a JavaScript objects property in three ways:
      var obj: Object = { age: 12 };
    
      var propName: string = "age";
      
      // These three lines are all equivalent
      obj.age = 3;
      obj["age"] = 3;    
      obj[propName] = 3;  // Use a variabel propName to select the property value to set
      
      // Use within a method
      ChangeValue(propName: string, value: any) : void
      {
      	// Assumes the property exists and is not undefined.
      	this._data[propName] = value;
      }
  2. To test if a property exists in an object, check if the property is undefined as follows:
      var propertyName: string = "name";
      if(this._data[propertyName] !== undefined)
      {
    	// The data field exists
      }
  3. You can use a boolean attribute e.g."_primaryKeySet", to determine whether the primary key value has been provided. Set it to true in SetData() if the key value is being set.