exists in near identical form on 14 pages. Even code which we have previously extracted such as the sortable behaviour are not necessarily included in the way we would like them to be.
We now need to apply that change to the entire site.
Removing ASP.NET Tags
The first item that is injected through ASP.net tags is the bug Id. Let's move that to a data attribute on the container holding all the controls. This is a logical place for everything inside the container is specific to that bug Id.
This can now be accessed from within the script by using:
As it turns out there is only a single function that is not used so we'll pull that out.
There is also a swath of code that appears to deal with changing the style of some drop downs. We handle all this in CSS since the redesign so we can removed that.
In so doing we need to put in references to the current object any time we reference a variable or function in the current object.
You'll notice that we've hoisted the variable definitions to the constructor of the new class which is already a big improvement on the construction of the old file.
We can now replace the initialization on the page with
This will create a new instance of EditBug and call the body load function upon it. Any calls on the page such as those attached to the various buttons will need to be changed to reference the page object. At the moment the calls are hard coded not just in the HTML but in the code behind.
We'll start by shifting the link construction to the .aspx file. Links, and especially styling such as the glyphicon class should not be in the C# code. The C# code can become
Which immediately looks much cleaner. The .aspx file now contains
Within the namespaced file you may notice that functions themselves are hung off the prototype of the class. This will save memory as the functions will not be recreated for each instance. An example looks like
I chose this particular function because what it does is find an element by using its tag and its name attribute. This is interesting because this sort of element selecting is something that jQuery is very good at. We mentioned before that we're going to drop support for older browsers. This function is also only used in one place to find a particular table cell.
In the spirit of always looking for easy ways to improve code as we're working with it we can replace this function call with
If you want to get extra fancy you don't even need to use jQuery and can replace the call with
which makes use of the selector API. jQuery will defer to this behavior if it is available and not buggy in the browser.
EditBug.js is over 400 lines long. While I don't have a particular limit in mind before a class becomes unmaintainable the nature of the code in this file gives me pause. The function are small but they are also largely unrelated. The vast majority of the functions don't call into other function which makes me think that the internal coupling in the class is low and so the class is not cohesive. Cohesion is important as it makes replacing the functionality in the future much easier. We can be confident that changes to a class that does nothing but handle e-mail address verification is not likely to change how web URLs are routed.
Digging into the class we find that there are a few different responsibilities in the class
- Warn when leaving the page if there are unsaved changes
- Getting and setting cookies
- Control the various buttons on the side of the page such as "Send E-mail"
- Setting and restoring presets
- Preventing the submit button being pushed twice
- Restyling drop downs
- Counting the number of characters in some fields to show the number remaining
- Deal with tags
- Add jQuery UI calendar to date fields
Goodness, that's quite a lot of stuff for one little file to do! Let's start breaking it out. Our goal is to end up with some reusable components that can be applied to different places in the application.
Marking the page as dirty is quite pervasive in the file. There are 10 places in the file that make some reference to the dirty state. Setting the dirty flag is done when a drop down changes or when a text area changes. Let's extract all of this dirty stuff and make a new file.
This DirtyFlag control takes a form and catches any changes to select, input and textarea elements in it. We can use it on our page like so:
This call can be placed in the EditBug.js file.
Next we can alter the warn_if_dirty function to observe the new isDirty property in the DirtyFlag control.
Finally we want to hook up the warn_if_dirty to any items on the page with the warn class.
Much of the logic related to dirty flags has now be moved out of the EditBug.js file and into a control that can be reused whenever we wish.
Preventing the submit button from being hit more than once is a pretty common sort of issue. Let's see if we can extract that functionality out into another class. We need to disable the button that was just pushed. The original code disables any submit buttons on the page. It seems highly unlikely that the page would take so long to load as to allow users to find the other submit button and click on it. Typically we just want to avoid an accidental double click. Let's stick to that behaviour as it allows us to easily encapsulate the functionality.
The ClickOnceButton looks like this
The first function is the constructor and hooks up the click action. The second function is the click action which is fired when a user clicks on a button. The button is disabled and the text on it is updated. This can be attached by simply calling it in the constructor of EditBug.js
We have a great, reusable component which can be used anywhere on the site.
A similar approach can be applied to extract the various other pieces of functionality from the overly large EditBugs files. A final example is the text area that shows a remaining count. This can quickly be extracted into a new control called LengthLimitedTextArea which looks like
These are the same functions as before with some jQuery tweaking. It can be wired up in our EditBug.js by simply newing it up in the constructor.
There are some other controls that could be extracted from this file. In classic text book style we'll leave these as an exercise for the reader.
The first step is to install the optimizations tools
This ScriptBundle will deal with minifying and concatinating the various scripts found under the Scripts/Site directory. The second parameter to the IncludeDirectory will include all the files that end with .js and the final parameters allows for a recursive search. We now need to plug that into the application start up in the Global.asax.cs.
The Application_OnStart method in that file is kind of a mess at the moment so we'll clean that up while we're in there. As we've mentioned before it pays to make code improvements any time you're in a file.
We can test this bundle by putting a file in the Scripts directory and trying to load the scripts. We can include the scripts in the master page by simply referencing the bundle like so
Now when we got to a page we can see that the scripts are indeed all loaded
In development all the scripts are included individually to aide in debugging. In production, which is achieved by toggling the flag in the web.config, the scripts are bundled