Data Management in the State Model
Back to the State Model, I have thought out and implemented the data management module.
Again, this post is part of the State Model Development series.
Data management is really at the heart of the State Model. The State Model is all about maintaining a data state, and notifying the user interface of any changes to the data, so this particular module is fairly important… though that’s not to say any of the others are less important!
I have released a sample implementation of dataManager.js. Same deal as last time, I haven’t tested it at all. There are likely tons of bugs, even minor syntax errors! I am just getting a skeletal implementation of everything out there so that I can test it as a whole, which I am aware is a terrible way to do things. I have also made a couple updates to queue.js, for some bug fixes and to reflect the dataManager implementation.
Also, these files will require JSON and util.js.
The data management model is actually rather simple. The difficult part was coming up with a solid plan for sending updates.
Within the dataManager object, all the named data fields is stored in an object called data.
The data objet’s fields are actually the named fields that this data object is managing. It’s my understanding that objects in javascript are implemented as hashtables, so this actually seems like the most efficient way to do this.
Each of the named fields is itself an object, with two fields, dependencies- representing the list of all ui elements that depend on this object and value- yet another object with the actual fields of the data. This means that data managed by dataManager should not be a raw piece of data, but rather named fields of data.
For example, if we have a data manager that is keeping track of only three pieces of information, the number of times I started my car today, ‘numStarts’, and some information about my computer, ‘computer’, and a list of my favorite hobbies ‘hobbies’, the data object might look something like this:
data = { numStarts : { count : 5 }, computer : { cores : 2, speedGHZ : 1.86, memoryGB : 2.96, name : 'Lisa', monitors : 1 }, hobbies : { list : ['hunting', 'fishing', 'kayaking', 'recreational sandwich eating'] } }
It might seem at first a little unnecessary, and I did consider alternatives, but in the end I thought this was the best way to go. Your own implementation of the State Model may behave differently, if you think that’s not reasonable.
We will now discuss updates. Updates can be made to the data by a call to the public method, update(name, updates, [wait]). Name is the name of the piece of data being referenced, and updates is an object containing one or more updateRequests. Wait is an optional value. If wait is set to true, ui components will not be notified of any changes until the next call to notifyDependencies();.
Wait was implemented so that when the queueManager is processing a series of updates, the same piece of data may get updated several times. It doesn’t make sense to re-render the same UI components until the data is actually a current reflection of the system state. It generally should not be used in the application code, but I suppose it may be useful.
Each field in the updates object should take one of the following forms:
fieldname : value fieldname : {method: updateMethod.set, value : value} fieldname : {method: updateMethod.append, value : value} fieldname : {method: updateMethod.remove, value : value} fieldname : {method: updateMethod.increment, amount : value}
The first two forms are actually the same thing: set a field to something else. For instance, if I wanted to change my computers name to Sammie and set up dual monitors, I might send a request like:
dataManager.update('computer', { name : 'Sammie', monitors : {method : updateMethod.set, value : 2} }
The second two require the field to be an array. If the field is not set, it will become an array. If it is set, and it is not an array, an exception will be thrown. If I recently discovered painting I make send a request like:
dataManager.update('hobbies', { list : {method : updateMethod.append, value : 'painting'} }
If on the other hand, I joined PETA, I might send:
dataManager.update('hobbies', { list : {method : updateMethod.remove, value : 'hunting'} }
Note that remove will actually remove all elements from the array equal to certain value. I am considering other ways to handle this so that it is not necessary to send the entire object to have it removed. One reasonable method would be to pass some type of a filter function or maybe an object to check fields against. Not quite sure yet, but this is one place where I will be updating the code before a final release.
The last form is useful for updating a numeric value without having to access it. You can increment by any value, positive or negative. So if I go back in time and start my car one less time today, I might send
dataManager.update( 'carStarts', { count : {method : updateMethod.increment, amount : -1} }
These updates can be sent by the application using the update method directly, but this actually brings me to how the server-side code should be sending data updates. The method is very similar. Keep in mind that the updateMethod values are actually enums with integer equivalents that should be set server-side as well as follows (set : 0, append : 1, remove : 2, increment : 3). An example response from the server to do some of the things we mentioned above might look something like:
[ { name : 'carStarts', update : { count : {method : updateMethod.increment, amount : -1} } }, { name : 'computer', update : { name : 'Sammie', monitors : {method : updateMethod.set, value : 2} } } ]
That basically covers setting values. Anytime a value is upated without the wait parameter set, the changes will automatically be propogated to the appropriate UI components, forcing the UI to be an accurate reflection of the system state.
Getting values from the data manager is actually more simple.
This is accomplished by the dataManager.get(name, [src]) . Name is the named field being requested, and src is an optional parameter, representing the UI component making the call. If the src parameter is set, the dependencies of this piece of data will be updated to include it.
At this particular moment, source is implemented as an action object paired with a parameters object. The dependencies list is an array containing these pairs. I think this is the wrong way to do this, and when I implement uiComponent, I might create some uiComponent id by which to reference it. This would make things a bit easier, and would also improve performance. Expect this to change.
An example call, if I wanted to know what my computer’s name was, might be like:
computer = dataManager.get('computer'); name = computer.name;
Well that’s about everything I have to say about data manager. Love to hear what anyone thinks. Email me.





approved.