Wednesday, November 30, 2011

the future

Joel rattled off a lot of what is in his head that is at the edge of my understanding today. It's the sort of stuff that makes me want to know more and feel kinda weak. I still haven't played with node.js let. I need to. The notion that JavaScript may seemlessly be both the front-end and the back-end scripting language for a web app is intriguing. In Windows 8 and .NET 5.0 there is and is to be capabilities that empower Windows development in HTML5 and JavaScript. popcorn.js empowers neat HTML5 metadata features for video by allowing events to be fired by script from key points in a video presentation. HTML5 has a database of sorts at the client (browser).

delete versus archive

We had a delete versus archive chat today. The consensus seemed to be that it was better to archive than delete. Why not delete a row from a database table? Well, who knows what was referencing it? Instead one may add an extra column, a bit for IsArchived, by which one may hide records away "forever," at each object. One downside is the extra column and the slight added complexity of queries. Another is lag that may occur just from having extra records in the database. One will have to craft all object queries to consider IsArchived.

T4 templates

T4 templates are .NET code generation tools. A T4 file has a .tt extension which one may right-click upon and pick "Run Custom Tool" to make the magic happen. Inside the .tt file is C# markup driving the magic. In our app we are using a T4 template to sniff the scope of an Entity Framework .edmx file and then render our corresponding NHibernate mapping files. This trick allows us to use the 1% of Entity Framework that is good along side of NHibernate.

   

Byron latches onto the .edmx like so:

string inputFile = @"ObjectModel.edmx";

   

Byron loops through entities in the .edmx like so:

foreach (EntityType entity in itemCollection.GetItems<EntityType>().OrderBy(e => e.Name))

lucid chart

https://www.lucidchart.com/ which runs in Chrome is a collaborative tool for doing Visioesque stuff.

get modern with .bind

Why not do this?

$('#thingy').click(function () {

   whatever();

});

 
 

Why "get modern" and do this instead?

$('#thingy').bind('click', function () {

   whatever();

});

 
 

.click is a convenience method wrapping .bind('click', and there isn't always going to be a convenience method.

$('#thingy').bind('somethinguncommon', function () {

   whatever();

});

.change event

An event for when a drop down list changes:

$("#myDropDownList").change(function () {

   adjustProgramPlansShuffleList();

});

Monday, November 28, 2011

HtmlEncode is the new addslashes()

Encode:

string EncodedString1 = HttpContext.Current.Server.HtmlEncode(foo);

 
 

Decode:

StringWriter myWriter = new StringWriter();

HttpUtility.HtmlDecode(foo, myWriter);

 
 

Use these as you might have used addslashes() and stripslashes() in 2004 when PHP was the better technology.

It's not 2004 anymore.


Sunday, November 27, 2011

break into HQL to empower NHibernate's eager loading

I stole the following from here as an example of how to eagerly load with NHibernate (and also how to break into HQL while returning a specific object type):

[Test]

public void Can_eagerly_load_order_aggregate_with_hql_query()

{

   Order fromDb;

   using (ISession session = SessionFactory.OpenSession())

   {

      string sql = "from Order o" +

            " inner join fetch o.OrderLines" +

            " inner join fetch o.Customer" +

            " where o.Id=:id";

      fromDb = session.CreateQuery(sql)

            .SetGuid("id", _order.Id)

            .UniqueResult<Order>();

   }

   Assert.IsTrue(NHibernateUtil.IsInitialized(fromDb.Customer));

   Assert.IsTrue(NHibernateUtil.IsInitialized(fromDb.OrderLines));

}

 
 

I also found this which suggests that the average implementation of Hibernate/NHibernate is going to use lazy loading by default.

I'm writing this post in follow up to this post.

Friday, November 25, 2011

=== allows you to determine if a JavaScript variable is undefined as opposed to null

I need to find if a variable exists yet or not in JavaScript/jQuery and I found this online offering this suggested approach:

if (window.x === undefined) alert("x is undefined/undeclared");

else if (x === false) alert("x is false");

else if (x === true) alert("x is true");

else alert("x is " + x);

   

I'm going to try it out. It means I'll be losing my triple equals virginity and making a strict comparison. This offers the following bullets on what the strict comparison operators (=== and !==) do:

  • Two strings are strictly equal when they have the same sequence of characters, same length, and same characters in corresponding positions.
  • Two numbers are strictly equal when they are numerically equal (have the same number value). NaN is not equal to anything, including NaN. Positive and negative zeros are equal to one another.
  • Two Boolean operands are strictly equal if both are true or both are false.
  • Two objects are strictly equal if they refer to the same Object.
  • Null and Undefined types are == (but not ===).

   

The last bullet really matters for what I want to do as what I want to do is:

if (window.x === undefined) alert("x is undefined/undeclared");

   

...and I do not want to match on null, only on undefined.

Why do I want to do this? I have a partial (the shuffle list control) that may be summoned numerous times over in a view. The partial has a blob of JavaScript in it declaring a variable. The last partial will sabotage all partials before it if the variable being declared is not unique so I need a means to make sure I am not using a variable I have used already.

Thursday, November 24, 2011

wish I understood AutoMapper better

AutoMapper is pretty cool. It spares you all of the right-hand, left-hand stuff of manually mapping properties of domain objects to view models. I wish I understood it better. We aim to use it more substantially in our app. I was asking Rafael some questions about it yesterday and he reinforced some of what I blogged of here.

  • typically Global.asax.cs references BootStrapper.cs
  • BootStrapper.cs initializes various profiles
  • typically an app will have one profile per project
  • a profile governs a particular concern (mapping POCOs to view models, mapping view models back to POCOs)
  • a profile will contain mapping rules if one cannot rely on naming convention for automapping (and one should try to do so)
    1. a formatter manages simple formatting rules
    2. a resolver requires a specific value and typically has more logic and dependencies to it

 
 

A example of what one might find in a profile:

Mapper.CreateMap<Account, AccountDetailViewModel>()

   .ForMember(x => x.AccountTypeName, y => y.MapFrom(z =>

         z.Division.AccountType.Name))

   .ForMember(x => x.DivisionName, y => y.MapFrom(z => z.Division.Name))

   .ForMember(x => x.ParentAccountName, y => y.MapFrom(z => z.ParentAccount

         != null ? z.ParentAccount.Name : string.Empty));

 
 

A example of coloring an action with an attribute: (a Foo is prepped in the action but a Bar is ultimately handed to the view at binding)

[AutoMap(typeof(Foo), typeof(Bar))]

public PartialViewResult Whatever(Guid id)

{

   var foo = FooRepository.GetById(id);

   foo.SomethingErOther = "this thing";

   return View("whatever", foo);

}

 
 

The attribute:

using System;

using System.Web.Mvc;

using AutoMapper;

namespace OurApp.Web.UI.ActionFilters

{

   [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]

   public class AutoMapAttribute : ActionFilterAttribute

   {

      private readonly Type _sourceType;

      private readonly Type _destType;

   

      public AutoMapAttribute(Type sourceType, Type destType)

      {

         _sourceType = sourceType;

         _destType = destType;

      }

   

      public override void OnActionExecuted(ActionExecutedContext filterContext)

      {

         var model = filterContext.Controller.ViewData.Model;

         var viewModel = Mapper.Map(model, _sourceType, _destType);

         filterContext.Controller.ViewData.Model = viewModel;

      }

   

      public Type SourceType

      {

         get { return _sourceType; }

      }

   

      public Type DestType

      {

         get { return _destType; }

      }

   }

}

 
 

A whiteboarding by Rafael:


At the left, an example of automapping:

var program = AutoMap.Map<SaveProgram, Program>(saveProgram);

Here we take an instance of SaveProgram called saveProgram and cast it to a Program called program.

 
 

At the right, showing how an object with a POCO getsetter may be flattened with its child into one simple view model without any getsetters that are not simple types.

conditional jQuery to do with where one is navigating to in a form for element B when departing element A

Our shuffle lists have a bug in which if something is selected at the left side when a form is submitted, it is added into the end result as though the item had been moved to the right side of the shuffle list.

Addendum 8/20/2015: I am commenting out http://a.yfrog.com/img610/9010/0ayd.jpg which yfrog has seen fit to replace with some sort of iTunes advertisement. I wish I had not started up my blog hosting images with these clowns.

 
 

I'm trying to fix this today. I have an idea of how to do so. I wrote my own little form in an attempt to iron out the jQuery needed:


<table>

   <tr>

      <td>

         <select multiple size="4" id="choices">

            <option value="foo">foo</option>

            <option value="bar">bar</option>

            <option value="baz">baz</option>

            <option value="qux">qux</option>

         </select>

      </td>

      <td>

         <input type="button" id="moveright" value=">" />

      </td>

      <td>

         <input id="fillintheblank" />

      </td>

   </tr>

   <tr>

      <td colspan="3" align="right">

         <input type="submit" id="submit" value="submit"/>

      </td>

   </tr>

</table>

<script type="text/javascript">

   $(function () {

      $('body').click(function () {

         emptylist();

      });

      $("#choices").click(function (e) {

         e.stopImmediatePropagation();

      });

      $("#moveright").click(function (e) {

         e.stopImmediatePropagation();

         uselistthenemptylist();

      }).keypress(function (e) {

         switch (e.keyCode) {

            case 13:

               uselistthenemptylist();

               return false;

         }

      }).blur(function (e) {

         emptylist();

      });

   });

   function emptylist() {

      var counter;

      var select = document.getElementById("choices");

      for (counter = 0; counter < select.options.length; counter++) {

         select.options[counter].selected = false;

      }

   }

   function uselistthenemptylist() {

      var contents = "";

      $("#choices option:selected").each(function () {

         contents += $(this).val();

      });

      $("#fillintheblank").val(contents);

      emptylist();

   }

</script>

 
 

Herein I want to:

  1. deselect all selections in select list in almost all circumstances when one leaves the select list for another point on the form in order to guarantee that nothing is selected when one submits the form
  2. empower the ability to move the contents of the select list to the text field by way of the button labeled with an arrow (a greater than sign) without such an ability being sabotaged by the first goal

The first item suggests a global rule while the second item is the exception.

My doings here work, though I have not yet had luck translating this success to our shuffle lists.

Things to note:

  1. e.stopImmediatePropagation(); keeps emptylist(); from happening at $('body').click in two exceptions to our global rule.
  2. .val() retrieves the hidden value of a line item in a select list while .text() retrieves the public-facing value
  3. return false; is used in wiring up what happens when one presses enter at the button labeled with an arrow when I might otherwise use break; as break; failed to allow copy to be moved into the text field.
  4. I cannot make a global .focus event and attach it to the body the way I can a .click event as the body will never leave focus, therefore, I have to ask myself how my process could be sabotaged by way of a key stroke.
    • One cannot submit the form by pressing return while the select list is the item of focus. Pressing return from such a vantage point would be like clicking the button labeled with an arrow.
    • One could tab to another element on the page so I had better wipe the contents of the select list in a .blur event for the select list. Wait! That would sabotage the button labeled with an arrow, so instead I will put the .blur event at the button labeled with an arrow. Thus one may tab once from the select list without values being lost, but not twice (and only in tabbing twice does a user enter a place where the return key will submit the form).

JavaScript Key Codes

http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes demystifies doing stuff like this in jQuery:

$('#SelectedSampleTypeId').keypress(function (e) {

   switch (e.keyCode) {

      case 38:

         whatever();

         break;

      case 40:

         whatever();

         break;

      case 13:

         whatever();

         break;

   }

});

Wednesday, November 23, 2011

Too much polymorphism for ActionLink?

@Html.ActionLink("Back To Program", "Details", "Program", new { Id = Model.ProgramPlan.Program.Id }, null) will take you to the Details action at the Program controller.

 
 

@Html.ActionLink("Back To Program", "Details", "Program", new { Id = Model.ProgramPlan.Program.Id }) will try to take you to the Details action at the current controller.

go to a different action and pass some variables too

return RedirectToAction("Edit", new { Id = id, ProgramId = programId });

using MvcHtmlString as a layer of C# between a Razor view and a call to a Razor partial

@Html.ShuffleList(properties here)

 
 

The code above in a Razor view reaches out to the following in HtmlHelperExtensions at OurApp.Web.UI\Helpers\HtmlHelper.cs which does not keep its contents inside of a using statement.

public static MvcHtmlString ShuffleList<properties here>(properties here)

{

   
code here

   return htmlHelper.Partial("_ShuffleList", model);

}

 
 

Note, at the last line we are grabbing the HTML out of a partial. The middle layer provides a forum to prep the model handed to the partial.

Tuesday, November 22, 2011

refactoring last week's work to accommodate the serialization of collections that make their way from C# to jQuery

Want to see what serializing a collection in C# to hand it to jQuery looks like? Well, I refactored the last two methods in ProgramPlanShuffleListFiltrationHelper to look like so:

   private void FlattenProgramPlansForPrograms(IEnumerable<Program> programs)

   {

      Dictionary<string, Guid> dictionary = new Dictionary<string, Guid>();

      foreach (Program program in programs)

      {

         List<ProgramPlan> dummyList = _programPlansForPrograms[program];

         foreach (ProgramPlan programPlan in dummyList)

         {

            string name = program.Name + "." + programPlan.NameStaticAttribute

                  .GetSelectedValueWithDefault(programPlan.Id).Value;

            dictionary.Add(name,programPlan.Id);

         }

      }

      var structure = dictionary.Select(d => new { Plan = d.Key, Id = d.Value });

      FlattenedListOfListsOfProgramPlansForPrograms =

            Newtonsoft.Json.JsonConvert.SerializeObject(structure);

   }

   

   private void FlattenProgramPlansForSampleTypes(IEnumerable<SampleType>

      sampleTypes)

   {

      List<List<string>> collection = new List<List<string>>();

      foreach (SampleType sampleType in sampleTypes)

      {

         List<ProgramPlan> dummyList = _programPlansForSampleTypes[sampleType];

         foreach (ProgramPlan programPlan in dummyList)

         {

            string name = programPlan.Program.Name + "." +

                  programPlan.NameStaticAttribute

                  .GetSelectedValueWithDefault(programPlan.Id).Value;

            string type = sampleType.Category.Name + "." + sampleType.Name;

            collection.Add(new List<string> { name, type });

         }

      }

      var structure = collection.Select(c => new { Plan = c[0], Type = c[1] });

      FlattenedListOfListsOfProgramPlansForSampleTypes =

            Newtonsoft.Json.JsonConvert.SerializeObject(structure);

   }

 
 

Want to see what the JSON going over the seam looks like? I had to change the two assert statements at the end of ProgramPlanShuffleListFiltrationHelperTest to be like this in order to make my test pass. (I did not otherwise need to modify the test.)

  1. Assert.AreEqual(programPlanShuffleListFiltrationHelper.FlattenedListOfListsOfProgramPlansForPrograms, "[{\"Plan\":\"MoneyMaker.Alpha\",\"Id\":\"" + alpha.Id + "\"},{\"Plan\":\"MoneyMaker.Beta\",\"Id\":\"" + beta.Id + "\"},{\"Plan\":\"MoneyMaker.Gamma\",\"Id\":\"" + gamma.Id + "\"},{\"Plan\":\"MoneyMaker.Delta\",\"Id\":\"" + delta.Id + "\"},{\"Plan\":\"CrowdPleaser.Epsilon\",\"Id\":\"" + epsilon.Id + "\"},{\"Plan\":\"CrowdPleaser.Zeta\",\"Id\":\"" + zeta.Id + "\"},{\"Plan\":\"CrowdPleaser.Eta\",\"Id\":\"" + eta.Id + "\"}]");
  2. Assert.AreEqual(programPlanShuffleListFiltrationHelper.FlattenedListOfListsOfProgramPlansForSampleTypes, "[{\"Plan\":\"MoneyMaker.Alpha\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"MoneyMaker.Beta\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"MoneyMaker.Gamma\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"CrowdPleaser.Epsilon\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"CrowdPleaser.Zeta\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"CrowdPleaser.Eta\",\"Type\":\"Silicon.CPU\"},{\"Plan\":\"MoneyMaker.Alpha\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"MoneyMaker.Gamma\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"MoneyMaker.Delta\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"CrowdPleaser.Epsilon\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"CrowdPleaser.Zeta\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"CrowdPleaser.Eta\",\"Type\":\"Silicon.GPU\"},{\"Plan\":\"MoneyMaker.Alpha\",\"Type\":\"Silicon.Chipset\"},{\"Plan\":\"CrowdPleaser.Eta\",\"Type\":\"Silicon.Chipset\"}]");

 
 

What do I need to do to wrangle the JSON on the jQuery side? I'm glad you asked! Below is a refactoring of the beginning of the "logic" method:

function logic(programsList, sampleTypesList, currentSampleType, currentPrograms) {

   var truncatedList = new Array();

   var $counterForSampleTypes = 0;

   var $counterForSampleTypeMatches = 0;

   $(sampleTypesList).each(function () {

      var presentType = this.Type;

      if (presentType == currentSampleType) {

         $counterForSampleTypeMatches++;

      }

      $counterForSampleTypes++;

   });

   var dummyStringForCreatingEmptyStringArray = 'replace me';

   if ($counterForSampleTypeMatches > 1) {

      var $counterForCreatingEmptyStringArray = 1;

      while ($counterForCreatingEmptyStringArray < $counterForSampleTypeMatches) {

         dummyStringForCreatingEmptyStringArray += ',replace me';

         $counterForCreatingEmptyStringArray++;

      }

   }

   truncatedList = dummyStringForCreatingEmptyStringArray.split(',');

   if ($counterForSampleTypeMatches > 0) {

      $counterForSampleTypes = 0;

      var $counterForPopulatingStringArray = 0;

      $(sampleTypesList).each(function () {

         var presentType = this.Type;

         var presentPlan = this.Plan;

         if (presentType == currentSampleType) {

            truncatedList[$counterForPopulatingStringArray] = presentPlan;

            $counterForPopulatingStringArray++;

         }

         $counterForSampleTypes++;

      });

   }

autocomplete="off"

...should work at both a form tag and an individual input tag.

It's what you think it is.

go to the History tab for a bug or task in Team Foundation Server to see who created it

Duh.

lazy loading versus eager fetching in NHibernate

I was surprised to learn that a method such as this one might invoke a trip to the database to find a program for a program plan!

public string Foo(ProgramPlan bar)

{

   return bar.Program.Name;

}

 
 

Why? NHibernate may be set to lazy load objects, getting just the objects and not their children. The properties on the object that would otherwise govern the children will instead contain proxy objects which fire off calls to your database to get what they need if someone pokes at them (but will be quiet and harmless otherwise).

Why lazy load? In the opposite scenario in which one eagerly fetches everything downstream of an object one could be pulling back and ignoramus swath of concerns. For example: if you eager load a foo and the foo has a collection of bars attached to it and each bar has a collection of bazes and each baz has a collection of quxes... well, without sounding too much like Doctor Seuss I think you can see the performance problem of getting everything when you may not really need it.

 
 

As I was going to St Ives

I met a man with seven wives

Every wife had seven sacks

Every sack had seven cats

Every cat had seven kittens

Kittens, cats, sacks, wives

How many were going to St Ives?

debugger;

Putting debugger; at a line in JavaScript will set a breakpoint in the Script tab within Firebug. From there one may set other breakpoints and step through code. Yay.

Monday, November 21, 2011

Stateful objects must really need to be stateful.

While I'm thinking about it, another thing which Joel mentioned that I could have done better here is that I could have had my helper return the end data instead of having it wearing the end data within getsetters. Joel asserts that a helper should not be stateful, it should be static. A class such as the one I made is a model and not a helper. I should err on the side of making any one object, regardless of the situation, static if possible. Stateful objects must really need to be stateful.

shelve a change in TFS

One may shelve a change in Team Foundation Server which is like tucking a change away instead of committing it. Do so just by right-clicking on your solution in the Solution Explorer and selecting "Shelve Pending Changes..."

Get shelved commit and integrate it into your code base by going to: File > Source Control > Unshelve Pending Changes...

Serialize objects to get them across the seam between C# and jQuery.

An audit of the work I've done here and here by Joel and Jorge has been forgiving and has largely found that I wasn't too off the mark in my implementation. Obviously, I should have used push to append items into arrays on the jQuery side. Another shortcoming is that I have not serialized objects when attempting to cross the seam between C# and jQuery. I learned how to do so today however. One may put a serialized object into string like so...

string Foo = Newtonsoft.Json.JsonConvert.SerializeObject(myArray);

 
 

...and then retrieve it, unflattened, in jQuery like so...

var flatPrograms = @Html.Raw(Model.Foo);

 
 

...JSON is used as the translation format. Serialize objects to get them across the seam between C# and jQuery. Do not flatten the objects on your own in C# and then unflatten them in jQuery. Duh. This is the better way to move collections across the divide.

push in jQuery appends a record to an array

You may not be able to append items to an array the way you can with a List<T> in C#, but jQuery, as it turns out, does not have such a shortcoming. Look:

var fruits = "apple,orange,banana";

fruitbasket = new Array();

fruitbasket = fruits.split(',');

fruitbasket.push("grape");

alert(fruitbasket[0]);

alert(fruitbasket[1]);

alert(fruitbasket[2]);

alert(fruitbasket[3]);

 
 

I was misguided and made some false assumptions when I crafted this. :)

tie .keypress as an event handler to a particular element in jQuery

When using the up and down arrows to traverse the items in a dropdown list, the focus event handler will not fire and the blur event handler will to fire until you leave the dropdown list. How then may you fire events while moving from one list item to another?

$('#myDropdown').keypress(function (e) {

   switch (e.keyCode) {

      case 38:

         alert('you clicked the up arrow key');

         break;

      case 40:

         alert('you clicked the down arrow key');

         break;

      case 13:

         alert('you pressed return');

         break;

   }

});

Sunday, November 20, 2011

coordinating completely anew

We just finished a night stand up by phone with our overseas team. Good to be scrumming together again. :)

legitimizing LinqSpecs

I interacted with José F. Romaniello, the author of LinqSpecs, over Twitter and learned that the project has been sitting in Alpha and untouched for over a year because he spun up the project and never had a chance to use it. That said, Mr. Romaniello offered that LinqSpecs were being used for heavy lifting in some sizable applications which helps bolster them. I think they are cool, and especially so now that I think we've figured out all of the pain points.

bridge the seam between C# and jQuery

I have been tasked with fixing a bug. The fix will involve handing data between C# and jQuery. Let me show you what I have so far. (I'm not done. I'm just getting started.) The bug happens on a CRUD screens for an object that is referred to as SiliconSampleDefinition at our application in code and is also referred to as simply "Silicon" in conversation as an instance of this object represents a piece of silicon.

Shuffle Lists:
We have created a control called a Shuffle List. It takes the shape of two select list controls capable of multiple selections sitting side by side. Buttons allow a user to move selected items from the list on the left to the list at the right and vice versa. The stuff on the left is stuff that may potentially be deemed as selected and the stuff on the right is stuff that actually is selected. I'm sure you've seen this sort of thing before.
Addendum 8/20/2015: I am commenting out http://a.yfrog.com/img610/9010/0ayd.jpg which yfrog has seen fit to replace with some sort of iTunes advertisement. I wish I had not started up my blog hosting images with these clowns.

...least I digress, back to silicon...

Silicon Relationships To Other Objects:
Addendum 8/20/2015: I am commenting out http://a.yfrog.com/img863/4500/qtfo.jpg which yfrog has seen fit to replace with some sort of iTunes advertisement. I wish I had not started up my blog hosting images with these clowns.
  1. Every piece of silicon is of one SampleType. There is a distinction between silicon for a CPU (Central Processing Unit) and a GPU (Graphics Processing Unit) for example and the SampleType records the distinction. At our create and edit screens for silicon, SampleType is represented by a drop down list which has an id of: SelectedSampleTypeId

     
  2. Pieces of silicon may be associated to any number of Programs. Programs are represented by a shuffle list. The right side of the shuffle list is a multiselect selection box with an id of: programsGroup-selection

     
  3. Finally, pieces of silicon may be associated to any number of ProgramPlans. Each ProgramPlan has one Program but each Program may have any number of ProgramPlans, a one-to-many relationship. ProgramPlans have a many-to-many relationship with SampleTypes which is to say that if a ProgramPlan is not of every SampleType then its scope is inherently restricted to a subset of the SampleTypes within our app. ProgramPlans also takes take shape in a shuffle list. In terms of id tags, let's say that the left half of the shuffle list is called "pp" and the right half is called "pps" for simplicity sake.
    Addendum 8/20/2015: I am commenting out http://a.yfrog.com/img610/9010/0ayd.jpg which yfrog has seen fit to replace with some sort of iTunes advertisement. I wish I had not started up my blog hosting images with these clowns.
     
The Problem:
The left side of the shuffle list starts with a list of a permissioned (making my own word here) ProgramPlans. The control allows one to select ProgramPlans which are not necessarily of the Program selected or the SampleType selected. No good. The values of Program and SampleType need to bias what is in both halves of the shuffle list for ProgramPlans. The values can change without reposting the page and thus the halves of the shuffle list for ProgramPlans are also going to have to change without reposting the page.

Before I began to tackle the problem, more ProgramPlans than were really needed, all ProgramPlans possibly applicable, were begin queried and made their way up to the UI for selection. If the list of ProgramPlans is going to be restrained then, arguably, we shouldn't query everything upfront. That said, I am going to punt on this problem in my first pass. Instead, I will make use of the current repository call of ProgramPlans. I will let everything make its way up to the UI, I will just change the way it comes up to the UI. I'm going to make things better incrementally. Later, if we need to use JSON/AJAX calls to get a subset of presently applicable ProgramPlans upon the change of Programs or Sample Types... well, that is a fight for another day.

I need to hand data to jQuery from C# which jQuery can somehow make sense of to determine what the possibilities for ProgramPlan are given a particular combination of Program and SampleType values. Again, I'm handing in all possible values for ProgramPlan. I end up creating an object that contains, as getsetters, two collections flattened into a pipe-delimited format. The values of the getsetters will bubble up into the UI where jQuery may consume them.

using System.Collections.Generic;

using System.Linq;

using OurApp.Core.Entities;

namespace OurApp.Web.UI.Util

{

   public class ProgramPlanShuffleListFiltrationHelper

   {

      public string FlattenedListOfListsOfProgramPlansForPrograms { get; private set; }

      public string FlattenedListOfListsOfProgramPlansForSampleTypes { get; private set; }

      private Dictionary<Program, List<ProgramPlan>> programPlansForPrograms {

            get; set; }

      private Dictionary<SampleType, List<ProgramPlan>> programPlansForSampleTypes {

            get; set; }

   

      public ProgramPlanShuffleListFiltrationHelper(List<Program> Programs,

            List<ProgramPlan> ProgramPlans, List<SampleType> SampleTypes)

      {

         programPlansForPrograms = Programs.ToDictionary(program => program,

               program => new List<ProgramPlan> { });

         programPlansForSampleTypes = SampleTypes.ToDictionary(sampleType =>

               sampleType, sampleType => new List<ProgramPlan> { });

         PopulateProgramPlansForPrograms(ProgramPlans);

         PopulateProgramPlansForSampleTypes(ProgramPlans, SampleTypes);

         FlattenProgramPlansForPrograms(Programs);

         FlattenProgramPlansForSampleTypes(SampleTypes);

      }

   

      private void PopulateProgramPlansForPrograms(List<ProgramPlan> ProgramPlans)

      {

         foreach (ProgramPlan programPlan in ProgramPlans)

         {

            List<ProgramPlan> dummyList =

                  programPlansForPrograms[programPlan.Program];

            dummyList.Add(programPlan);

            programPlansForPrograms[programPlan.Program] = dummyList;

         }

      }

   

      private void PopulateProgramPlansForSampleTypes(List<ProgramPlan>

            ProgramPlans, List<SampleType> SampleTypes)

      {

         foreach (ProgramPlan programPlan in ProgramPlans)

         {

            foreach (SampleType sampleType in programPlan.SampleTypes)

            {

               if (SampleTypes.Count > 0 && SampleTypes.Contains(sampleType))

               {

                  List<ProgramPlan> dummyList =

                        programPlansForSampleTypes[sampleType];

                  dummyList.Add(programPlan);

                  programPlansForSampleTypes[sampleType] = dummyList;

               }

            }

         }

      }

   

      private void FlattenProgramPlansForPrograms(List<Program> Programs)

      {

         foreach (Program program in Programs)

         {

            List<ProgramPlan> dummyList = programPlansForPrograms[program];

            foreach (ProgramPlan programPlan in dummyList)

            {

               if (FlattenedListOfListsOfProgramPlansForPrograms == null)

               {

                  FlattenedListOfListsOfProgramPlansForPrograms = "";

               } else {

                  FlattenedListOfListsOfProgramPlansForPrograms += "|";

               }

               FlattenedListOfListsOfProgramPlansForPrograms += program.Name + ".";

               FlattenedListOfListsOfProgramPlansForPrograms +=

                     programPlan.NameStaticAttribute

                     .GetSelectedValueWithDefault(programPlan.Id).Value;

               FlattenedListOfListsOfProgramPlansForPrograms += "," + programPlan.Id;

            }

         }

      }

   

      private void FlattenProgramPlansForSampleTypes(List<SampleType> SampleTypes)

      {

         foreach (SampleType sampleType in SampleTypes)

         {

            List<ProgramPlan> dummyList = programPlansForSampleTypes[sampleType];

            foreach (ProgramPlan programPlan in dummyList)

            {

               if (FlattenedListOfListsOfProgramPlansForSampleTypes == null)

               {

                  FlattenedListOfListsOfProgramPlansForSampleTypes = "";

               } else {

                  FlattenedListOfListsOfProgramPlansForSampleTypes += "|";

               }

               FlattenedListOfListsOfProgramPlansForSampleTypes +=

                     programPlan.Program.Name + ".";

               FlattenedListOfListsOfProgramPlansForSampleTypes +=

                     programPlan.NameStaticAttribute

                     .GetSelectedValueWithDefault(programPlan.Id).Value;

               FlattenedListOfListsOfProgramPlansForSampleTypes += "," +

                     sampleType.Category.Name;

               FlattenedListOfListsOfProgramPlansForSampleTypes += "." +

                     sampleType.Name;

            }

         }

      }

   }

}

 
 

I get the above under test like so:

using System.Collections.Generic;

using OurApp.Core.Entities;

using OurApp.Data.Tests.Support;

using OurApp.Web.UI.Util;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace OurApp.Data.Tests.Utils

{

   [TestClass]

   public class ProgramPlanShuffleListFiltrationHelperTest : DataIntegrationTestBase

   {

      [TestMethod]

      public void test_facets_of_ProgramPlanShuffleListFiltrationHelperTest()

      {

         Category silicon = new Category {Name = "Silicon", IdPrefix = "S" };

         silicon.Save();

         SampleType cpu = new SampleType { Name = "CPU", IdPrefix="CPU", Category =

               silicon };

         cpu.Save();

         SampleType gpu = new SampleType { Name = "GPU", IdPrefix = "GPU", Category

               = silicon };

         gpu.Save();

         SampleType chipset = new SampleType { Name = "Chipset", IdPrefix = "Chipset",

               Category = silicon };

         chipset.Save();

   

         ProgramPlan alpha = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType>{ cpu, gpu, chipset } };

         alpha.Save();

         alpha.NameStaticAttribute.CreateAndAddAvailableValue("Alpha")

               .SelectValueForEntity(alpha.Id);

         alpha.Save();

   

         ProgramPlan beta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu } };

         beta.Save();

         beta.NameStaticAttribute.CreateAndAddAvailableValue("Beta")

               .SelectValueForEntity(beta.Id);

         beta.Save();

   

         ProgramPlan gamma = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         gamma.Save();

         gamma.NameStaticAttribute.CreateAndAddAvailableValue("Gamma")

               .SelectValueForEntity(gamma.Id);

         gamma.Save();

   

         ProgramPlan delta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { gpu } };

         delta.Save();

         delta.NameStaticAttribute.CreateAndAddAvailableValue("Delta")

               .SelectValueForEntity(delta.Id);

         delta.Save();

   

         ProgramPlan epsilon = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         epsilon.Save();

         epsilon.NameStaticAttribute.CreateAndAddAvailableValue("Epsilon")

               .SelectValueForEntity(epsilon.Id);

         epsilon.Save();

   

         ProgramPlan zeta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         zeta.Save();

         zeta.NameStaticAttribute.CreateAndAddAvailableValue("Zeta")

               .SelectValueForEntity(zeta.Id);

         zeta.Save();

   

         ProgramPlan eta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu, chipset } };

         eta.Save();

         eta.NameStaticAttribute.CreateAndAddAvailableValue("Eta")

               .SelectValueForEntity(eta.Id);

         eta.Save();

   

         Program moneyMaker = new Program { Name = "MoneyMaker", ProgramPlans =

               new List<ProgramPlan>() {alpha, beta, gamma, delta } };

         moneyMaker.Save();

         Program crowdPleaser = new Program { Name = "CrowdPleaser", ProgramPlans =

               new List<ProgramPlan>() { epsilon, zeta, eta } };

         crowdPleaser.Save();

   

         alpha.Program = moneyMaker;

         alpha.Save();

         beta.Program = moneyMaker;

         beta.Save();

         gamma.Program = moneyMaker;

         gamma.Save();

         delta.Program = moneyMaker;

         delta.Save();

         epsilon.Program = crowdPleaser;

         epsilon.Save();

         zeta.Program = crowdPleaser;

         zeta.Save();

         eta.Program = crowdPleaser;

         eta.Save();

   

         ProgramPlanShuffleListFiltrationHelper programPlanShuffleListFiltrationHelper =

               new ProgramPlanShuffleListFiltrationHelper(new List<Program>{ moneyMaker,

               crowdPleaser }, new List<ProgramPlan>{ alpha, beta, gamma, delta, epsilon,

               zeta, eta }, new List<SampleType>{ cpu, gpu, chipset });

         Assert.AreEqual(programPlanShuffleListFiltrationHelper

               .FlattenedListOfListsOfProgramPlansForPrograms, "MoneyMaker.Alpha," +

               alpha.Id + "|MoneyMaker.Beta," + beta.Id + "|MoneyMaker.Gamma," +

               gamma.Id + "|MoneyMaker.Delta," + delta.Id + "|CrowdPleaser.Epsilon," +

               epsilon.Id + "|CrowdPleaser.Zeta," + zeta.Id + "|CrowdPleaser.Eta," + eta.Id);

         Assert.AreEqual(programPlanShuffleListFiltrationHelper

               .FlattenedListOfListsOfProgramPlansForSampleTypes,

               "MoneyMaker.Alpha,Silicon.CPU|MoneyMaker.Beta,Silicon.CPU|MoneyMaker.

               Gamma,Silicon.CPU|CrowdPleaser.Epsilon,Silicon.CPU|CrowdPleaser.Zeta,

               Silicon.CPU|CrowdPleaser.Eta,Silicon.CPU|MoneyMaker.Alpha,Silicon.GPU|

               MoneyMaker.Gamma,Silicon.GPU|MoneyMaker.Delta,Silicon.GPU|

               CrowdPleaser.Epsilon,Silicon.GPU|CrowdPleaser.Zeta,Silicon.GPU|

               CrowdPleaser.Eta,Silicon.GPU|MoneyMaker.Alpha,Silicon.Chipset|

               CrowdPleaser.Eta,Silicon.Chipset");

      }

   }

}

 
 

The last two lines of the test above, the assert statements, show what the flattened data I will hand to jQuery looks like.

I have a second post here, that shows off what I do with the data on the jQuery side.

You will notice that I just unflatten what I've flattened in C# in jQuery. I'm not sure of how else to hand a collection into jQuery from C# without JSON. Perhaps JSON was the way I should have gone. Hmmm.

Addendum 8/20/2015: I am commenting out http://a.yfrog.com/img877/6388/u73m.jpg which yfrog has seen fit to replace with some sort of iTunes advertisement. I wish I had not started up my blog hosting images with these clowns.

Working in jQuery really makes me appreciate (miss) the generics of C#!

@using OurApp.Web.UI.Util

@model ProgramPlanShuffleListFiltrationHelper

<script src="@Url.Content("~/Scripts/jquery.timers-1.2.js")" type="text/javascript">

</script>

<script type="text/javascript">

   var flatPrograms = "@Model.FlattenedListOfListsOfProgramPlansForPrograms";

   var flatSampleTypes = "@Model.FlattenedListOfListsOfProgramPlansForSampleTypes";

   var endList = new Array();

   var oldList = new Array();

   

   $(function () {

      $(document).everyTime(3000, function (i) {

         prepareProgramPlanList(flatPrograms.split('|'), flatSampleTypes.split('|'));

         setOldList();

         revampCurrentlySelectedProgramPlans();

         setOldList();

         revampCurrentlyAvailableProgramPlans();

      }, 0);

   });

   

   function revampCurrentlyAvailableProgramPlans() {

      var revisionToOldList = new Array();

      var $counterForRevisedList = 0;

      var $counterForEndList = 0;

      while ($counterForEndList < endList.length) {

         var halvesOfProgram = endList[$counterForEndList].split(',');

         var $counterForOldList = 0;

         var isMatch = false;

         while ($counterForOldList < oldList.length) {

            if (oldList[$counterForOldList] == halvesOfProgram[0]) {

               isMatch = true;

            }

            $counterForOldList++;

         }

         if (!isMatch) {

            $counterForRevisedList++;

         }

         $counterForEndList++;

      }

      if ($counterForRevisedList > 0) {

         var dummyStringForCreatingEmptyStringArray = 'replace me';

         if ($counterForRevisedList > 1) {

            var $counterForCreatingEmptyStringArray = 1;

            while ($counterForCreatingEmptyStringArray < $counterForRevisedList) {

               dummyStringForCreatingEmptyStringArray += ',replace me';

               $counterForCreatingEmptyStringArray++;

            }

         }

         revisionToOldList = dummyStringForCreatingEmptyStringArray.split(',');

      }

      $counterForEndList = 0;

      $counterForRevisedList = 0;

      while ($counterForEndList < endList.length) {

         var halvesOfProgram = endList[$counterForEndList].split(',');

         var $counterForOldList = 0;

         var isMatch = false;

         while ($counterForOldList < oldList.length) {

            if (oldList[$counterForOldList] == halvesOfProgram[0]) {

               isMatch = true;

            }

            $counterForOldList++;

         }

         if (!isMatch) {

            revisionToOldList[$counterForRevisedList] = endList[$counterForEndList];

            $counterForRevisedList++;

         }

         $counterForEndList++;

      }

      $("#pp option").remove();

      $counterForRevisedList = 0;

      while ($counterForRevisedList < revisionToOldList.length) {

         var item = revisionToOldList[$counterForRevisedList];

         var x = item.split(',');

         $("#pp").append($('<option></option>').val(x[1]).html(x[0]));

         $counterForRevisedList++;

      }

   }

   

   function revampCurrentlySelectedProgramPlans() {

      var revisionToOldList = new Array();

      var $counterForRevisedList = 0;

      var $counterForEndList = 0;

      while ($counterForEndList < endList.length) {

         var halvesOfProgram = endList[$counterForEndList].split(',');

         var $counterForOldList = 0;

         while ($counterForOldList < oldList.length) {

            if (oldList[$counterForOldList] == halvesOfProgram[0]) {

               $counterForRevisedList++;

            }

            $counterForOldList++;

         }

         $counterForEndList++;

      }

      if ($counterForRevisedList > 0) {

         var dummyStringForCreatingEmptyStringArray = 'replace me';

         if ($counterForRevisedList > 1) {

            var $counterForCreatingEmptyStringArray = 1;

            while ($counterForCreatingEmptyStringArray < $counterForRevisedList) {

               dummyStringForCreatingEmptyStringArray += ',replace me';

               $counterForCreatingEmptyStringArray++;

            }

         }

         revisionToOldList = dummyStringForCreatingEmptyStringArray.split(',');

      }

      $counterForEndList = 0;

      $counterForRevisedList = 0;

      while ($counterForEndList < endList.length) {

         var halves = endList[$counterForEndList].split(',');

         var $counterForList = 0;

         while ($counterForList < oldList.length) {

            if (oldList[$counterForList] == halves[0]) {

               revisionToOldList[$counterForRevisedList] = endList[$counterForEndList];

               $counterForRevisedList++;

            }

            $counterForList++;

         }

         $counterForEndList++;

      }

      $("#pps option").remove();

      $counterForRevisedList = 0;

      while ($counterForRevisedList < revisionToOldList.length) {

         var item = revisionToOldList[$counterForRevisedList];

         var x = item.split(',');

         $("#pps").append($('<option></option>').val(x[1]).html(x[0]));

         $counterForRevisedList++;

      }

   }

   

   function setOldList() {

      var countOfPlans = $("#pps").get(0).options.length;

      oldList = new Array();

      if (countOfPlans > 0) {

         var dummyStringForCreatingEmptyStringArray = 'replace me';

         if (countOfPlans > 1) {

            var $counterForCreatingEmptyStringArray = 1;

            while ($counterForCreatingEmptyStringArray < countOfPlans) {

               dummyStringForCreatingEmptyStringArray += ',replace me';

               $counterForCreatingEmptyStringArray++;

            }

         }

         oldList = dummyStringForCreatingEmptyStringArray.split(',');

      }

      if (countOfPlans > 0) {

         var $counterForPopulatingStringArray = 0;

         $("#pps option").each(function () {

            oldList[$counterForPopulatingStringArray] = $(this).text();

            $counterForPopulatingStringArray++;

         });

      }

   }

   

   function prepareProgramPlanList(programsList, sampleTypesList) {

      var currentSampleType = $("#SelectedSampleTypeId option:selected").text();

      var countOfPrograms = $("#programsGroup-selection").get(0).options.length;

      var currentPrograms = new Array();

      if (countOfPrograms > 0) {

         var dummyStringForCreatingEmptyStringArray = 'replace me';

         if (countOfPrograms > 1) {

            var $counterForCreatingEmptyStringArray = 1;

            while ($counterForCreatingEmptyStringArray < countOfPrograms) {

               dummyStringForCreatingEmptyStringArray += ',replace me';

               $counterForCreatingEmptyStringArray++;

            }

         }

         currentPrograms = dummyStringForCreatingEmptyStringArray.split(',');

      }

      if (countOfPrograms > 0) {

         var $counterForPopulatingStringArray = 0;

         $("#programsGroup-selection option").each(function () {

            currentPrograms[$counterForPopulatingStringArray] = $(this).text();

            $counterForPopulatingStringArray++;

         });

      }

      logic(programsList, sampleTypesList, currentSampleType, currentPrograms);

   }

   

   function logic(programsList, sampleTypesList, currentSampleType, currentPrograms) {

      var truncatedList = new Array();

      var $counterForSampleTypes = 0;

      var $counterForSampleTypeMatches = 0;

      $(sampleTypesList).each(function () {

         var halvesOfSampleType = sampleTypesList[$counterForSampleTypes].split(',');

         if (halvesOfSampleType[1] == currentSampleType) {

            $counterForSampleTypeMatches++;

         }

         $counterForSampleTypes++;

      });

      var dummyStringForCreatingEmptyStringArray = 'replace me';

      if ($counterForSampleTypeMatches > 1) {

         var $counterForCreatingEmptyStringArray = 1;

         while ($counterForCreatingEmptyStringArray < $counterForSampleTypeMatches) {

            dummyStringForCreatingEmptyStringArray += ',replace me';

            $counterForCreatingEmptyStringArray++;

         }

      }

      truncatedList = dummyStringForCreatingEmptyStringArray.split(',');

      if ($counterForSampleTypeMatches > 0) {

         $counterForSampleTypes = 0;

         var $counterForPopulatingStringArray = 0;

         $(sampleTypesList).each(function () {

            var halvesOfSampleType = sampleTypesList[$counterForSampleTypes].split(',');

            if (halvesOfSampleType[1] == currentSampleType) {

               truncatedList[$counterForPopulatingStringArray] = halvesOfSampleType[0];

               $counterForPopulatingStringArray++;

            }

            $counterForSampleTypes++;

         });

      }

      var $counterForIntersectionList = 0;

      var $counterForAllPrograms = 0;

      $(programsList).each(function () {

         var halvesOfProgram = programsList[$counterForAllPrograms].split(',');

         var $counterForTruncatedList = 0;

         $(truncatedList).each(function () {

            if (halvesOfProgram[0] == truncatedList[$counterForTruncatedList]) {

               $counterForIntersectionList++;

            }

            $counterForTruncatedList++;

         });

         $counterForAllPrograms++;

      });

      var intersectionList = new Array();

      dummyStringForCreatingEmptyStringArray = 'replace me';

      if ($counterForIntersectionList > 1) {

         $counterForCreatingEmptyStringArray = 1;

         while ($counterForCreatingEmptyStringArray < $counterForIntersectionList) {

            dummyStringForCreatingEmptyStringArray += ',replace me';

            $counterForCreatingEmptyStringArray++;

         }

      }

      if ($counterForIntersectionList > 0) {

         intersectionList = dummyStringForCreatingEmptyStringArray.split(',');

      }

      if ($counterForIntersectionList > 0) {

         $counterForAllPrograms = 0;

         var $counter = 0;

         $(programsList).each(function () {

            var halvesOfProgram = programsList[$counterForAllPrograms].split(',');

            var $counterForTruncatedList = 0;

            $(truncatedList).each(function () {

               if (halvesOfProgram[0] == truncatedList[$counterForTruncatedList]) {

                  intersectionList[$counter] = programsList[$counterForAllPrograms];

                  $counter++;

               }

               $counterForTruncatedList++;

            });

            $counterForAllPrograms++;

         });

      }

      var $counterForEndList = 0;

      $counterForAllPrograms = 0;

      $(intersectionList).each(function () {

         var halvesOfProgram = intersectionList[$counterForAllPrograms].split(',');

         var quartersOfProgram = halvesOfProgram[0].split('.');

         var $counterForCurrentPrograms = 0;

         $(currentPrograms).each(function () {

            if (quartersOfProgram[0] == currentPrograms[$counterForCurrentPrograms]) {

               $counterForEndList++;

            }

            $counterForCurrentPrograms++;

         });

         $counterForAllPrograms++;

      });

      endList = new Array();

      dummyStringForCreatingEmptyStringArray = 'replace me';

      if ($counterForEndList > 1) {

         $counterForCreatingEmptyStringArray = 1;

         while ($counterForCreatingEmptyStringArray < $counterForEndList) {

            dummyStringForCreatingEmptyStringArray += ',replace me';

            $counterForCreatingEmptyStringArray++;

         }

      }

      if ($counterForEndList > 0) {

         endList = dummyStringForCreatingEmptyStringArray.split(',');

      }

      $counterForEndList = 0;

      $counterForAllPrograms = 0;

      $(intersectionList).each(function () {

         var halvesOfProgram = intersectionList[$counterForAllPrograms].split(',');

         var quartersOfProgram = halvesOfProgram[0].split('.');

         var $counterForCurrentPrograms = 0;

         $(currentPrograms).each(function () {

            if (quartersOfProgram[0] == currentPrograms[$counterForCurrentPrograms]) {

               endList[$counterForEndList] = intersectionList[$counterForAllPrograms];

               $counterForEndList++;

            }

            $counterForCurrentPrograms++;

         });

         $counterForAllPrograms++;

      });

   }

</script>

 
 

Some notes on the fire hose of code above which represents the contents of an ASP.NET MVC3 partial:

  • Shouldn't most of this code be kept in a .js file? Well, yes, most of it should. I am part way through crafting a solution and in the short term I just want to get things working. I'll do the abstraction to a .js file as a last step. Lines 6 and 7 above which are a seam between C# and jQuery need to remain in the Razor view. They are:

    @Model.FlattenedListOfListsOfProgramPlansForPrograms

    @Model.FlattenedListOfListsOfProgramPlansForSampleTypes


       
  • So what is bubbling up from C# to jQuery? I'll explain in the next post.

       
  • Is the timer loop running between lines 11 and 19 by way of, as referenced in line 3: jquery.timers-1.2.js ...a good idea? No, it's a terrible implementation. This is not the way to empower the code it empowers. A better way would be to run the same concerns upon the click events of certain key elements within the view containing the partial. I just got the timer working in the short term as it was easy to wire up quickly to have something to show my superiors. (I'm not done with this yet.) Don't do this:

    $(function () {

       $(document).everyTime(3000, function (i) {

          prepareProgramPlanList(flatPrograms.split('|'), flatSampleTypes.split('|'));

          setOldList();

          revampCurrentlySelectedProgramPlans();

          setOldList();

          revampCurrentlyAvailableProgramPlans();

       }, 0);

    });


       
  • What is the point of this blog post? I hope it expositions some of the pain of creating new collections based upon other collections. If someone knows a better way to approach this problem, please let me know. I am new to this. There are numerous places herein where I am using a set of three sequential loops:
    1. I loop through an existing collection trying to find items that "make a match" and... for every match I increment a counter up one from zero.
    2. I create a dummy string. The string will have a number of commas in it equal to a number one less than number I ended up with in the counter. It will look like so "replace me,replace me,replace me" and will allow me to ultimately create an array with the appropriate number of records by way of .split(',');
    3. I go through a loop not unlike the first loop in which I change out the replace me values with what I really want in the array.
       
  • The problem is that as the first loop runs there is no way to know just how big the array I am to make should be and thus I have to define it by incrementing a counter and then creating an array based upon the counter's count. I may then "rerun" the first loop, so to speak, and just put data in my new array.

       
  • Isn't it awesome how with a List<T> in C# that one doesn't need to set a finite count size. One can always just incrementally add items onto the end of the list. If I could do something like that here I could do everything in one loop. Does anyone know how to address this problem in jQuery?

       
  • What else is interesting in the code above? Well, it shows how to pull the contents from a drop down list.

    $("#pps option").each(function () {

       oldList[$counterForPopulatingStringArray] = $(this).text();

       $counterForPopulatingStringArray++;

    });


       
  • Use $(this).val(); instead of $(this).text(); to get the "real" value of an item instead "public facing" one that the user sees.

       
  • Fill a drop down list with the contents of an array like so:

    $("#pps").append($('<option></option>').val(x[1]).html(x[0]));

       
  • Empty a drop down list:

    $("#pps option").remove();

       
  • Get the number of items in a drop down list:

    var countOfPrograms = $("#programsGroup-selection").get(0).options.length;

       
  • Get the number of items in an array:

    while ($counterForEndList < endList.length) {

       
  • Type cast a variable as an array:

    var endList = new Array();