$selected.Visual.DataContext.ModelCollection | Select OperationId, OperatingDate, OperationStatus | Format-Table -autosize OperationId OperatingDate OperationStatus ----------- ------------- --------------- 2076 Urgent 70 Urgent 2162 23.10.2013 00:00:00 Operation 2165 23.10.2013 00:00:00 Operation 2196 23.10.2013 00:00:00 Operation 2197 23.10.2013 00:00:00 Operation 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 0 23.10.2013 00:00:00 None 1913 Urgent 2192 23.10.2013 00:00:00 Operation 2193 23.10.2013 00:00:00 Operation 2194 23.10.2013 00:00:00 Operation 2013 23.10.2013 00:00:00 Operation 2195 23.10.2013 00:00:00 Operation 2161 23.10.2013 00:00:00 OperationIt could be possible to pipe this further to the Export-Csv Powershell cmdlet and you can have Excel launch the resulting .csv file. This shows how SnoopWpf can be used to do powerful runtime analysis and inspection of WPF applications and also support Powershell operators. Both technical testers and WPF developers will find this useful. To get started, install Snoop Wpf created by the brilliant developer Cory Plotts and can be downloaded from here: Snoop WPF
Thursday, 24 October 2013
Using Snoop and PowerShell to interrogate View Models in WPF applications
It is possible to use Snoop to interrogate View Models in WPF applications. If you first install Snoop, check that you run Snoop as an administrator, if you cannot
attach to the process which is your WPF application. Start snooping the application, then move to the tab Powershell. Use Ctrl+Shift and click on the visual or ui element
you want to inspect in the GUI. You will see a pink border around the ui element or visual and this will then be possible to inspect further. Go to the PowerShell
tab and start interrogating the object.
In the MVVM WPF application at my work, we can see the view model in the property DataContext.ViewModel. It is possible to further drill down to properties in the ViewModel
quite easily.
Example follows, I have selected a RadGridView in my application and select the Visual property, then look at the DataContext of the Visual, drill down further to the
ModelCollection property, which in this particular example is a property of the data context object of which the selected visual is data bound to. This object contains
a property of type ObservableCollection which has got some custom object inside. The collection object is then piped to the Select operator in Powershell and this Select operator projects the properties of each object in the ModelCollection, in this particular case I select OperationId, OperatingDate and OperationStatus. This is then further piped to Format-Table which is used with the argument -autoresize. The resulting selector is then:
Friday, 26 July 2013
Creating a simple autocomplete control with Bootstrap for with a remote JSON service
I have worked a bit with the Twitter Bootstrap javascript library / framework and testet out the TypeAhead control. I could not find a complete
example of how to get the autocomplete items from a remote JSON service, so I testet it out myself.
Let's first define a web service (ASP.NET) which will return the list of sovereign states in the world:
//using System; //using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web; using System.Web.Script.Services; using System.Web.Services; namespace TestBootstrapCss { /// <summary> /// Summary description for CountryService /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] [System.Web.Script.Services.ScriptService] public class CountryService : System.Web.Services.WebService { [WebMethod] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public Country[] GetCountries(string query) { if (cachedCountryList == null) cachedCountryList = GetCountryList(); var result = cachedCountryList.Where(c => c.Name.StartsWith(query, System.StringComparison.CurrentCultureIgnoreCase)).ToArray(); return result; } public class Country { public int Id { get; set; } public string Name { get; set; } public int Population { get; set; } public double WorldPercentage { get; set; } } private static Country[] cachedCountryList = null; public Country[] GetCountryList() { return new Country[] { new Country { Id =1,Name = "China",Population = 1354040000 ,WorldPercentage = 19.07 }, new Country { Id =2,Name = "India",Population = 1210569573 ,WorldPercentage = 17.05 }, new Country { Id =3,Name = "United States",Population = 316318000 ,WorldPercentage = 4.45 }, new Country { Id =4,Name = "Indonesia",Population = 237641326 ,WorldPercentage = 3.35 }, new Country { Id =5,Name = "Brazil",Population = 193946886 ,WorldPercentage = 2.73 }, new Country { Id =6,Name = "Pakistan",Population = 183766000 ,WorldPercentage = 2.59 }, new Country { Id =7,Name = "Nigeria",Population = 173615000 ,WorldPercentage = 2.45 }, new Country { Id =8,Name = "Bangladesh",Population = 152518015 ,WorldPercentage = 2.15 }, new Country { Id =9,Name = "Russia",Population = 143400000 ,WorldPercentage = 2.02 }, new Country { Id =10,Name = "Japan",Population = 127350000 ,WorldPercentage = 1.79 }, new Country { Id =11,Name = "Mexico",Population = 118419000 ,WorldPercentage = 1.67 }, new Country { Id =12,Name = "Philippines",Population = 98100000 ,WorldPercentage = 1.38 }, new Country { Id =13,Name = "Vietnam",Population = 88780000 ,WorldPercentage = 1.25 }, new Country { Id =14,Name = "Ethiopia",Population = 86613986 ,WorldPercentage = 1.22 }, new Country { Id =15,Name = "Egypt",Population = 83661000 ,WorldPercentage = 1.18 }, new Country { Id =16,Name = "Germany",Population = 80493000 ,WorldPercentage = 1.13 }, new Country { Id =17,Name = "Iran",Population = 76768000 ,WorldPercentage = 1.08 }, new Country { Id =18,Name = "Turkey",Population = 75627384 ,WorldPercentage = 1.07 }, new Country { Id =19,Name = "Democratic Republic of the Congo",Population = 67514000 ,WorldPercentage = 0.95 }, new Country { Id =20,Name = "Thailand",Population = 65926261 ,WorldPercentage = 0.93 }, new Country { Id =21,Name = "France[9]",Population = 65684000 ,WorldPercentage = 0.92 }, new Country { Id =22,Name = "United Kingdom",Population = 63181775 ,WorldPercentage = 0.89 }, new Country { Id =23,Name = "Italy",Population = 59704082 ,WorldPercentage = 0.84 }, new Country { Id =24,Name = "Myanmar",Population = 53259000 ,WorldPercentage = 0.75 }, new Country { Id =25,Name = "South Africa",Population = 52981991 ,WorldPercentage = 0.75 }, new Country { Id =26,Name = "South Korea",Population = 50219669 ,WorldPercentage = 0.71 }, new Country { Id =27,Name = "Colombia",Population = 47159000 ,WorldPercentage = 0.66 }, new Country { Id =28,Name = "Spain",Population = 47059533 ,WorldPercentage = 0.66 }, new Country { Id =29,Name = "Ukraine",Population = 45495252 ,WorldPercentage = 0.64 }, new Country { Id =30,Name = "Tanzania",Population = 44928923 ,WorldPercentage = 0.63 }, new Country { Id =31,Name = "Kenya",Population = 44354000 ,WorldPercentage = 0.62 }, new Country { Id =32,Name = "Argentina",Population = 40117096 ,WorldPercentage = 0.57 }, new Country { Id =33,Name = "Poland",Population = 38533299 ,WorldPercentage = 0.54 }, new Country { Id =34,Name = "Sudan",Population = 37964000 ,WorldPercentage = 0.53 }, new Country { Id =35,Name = "Algeria",Population = 37900000 ,WorldPercentage = 0.53 }, new Country { Id =36,Name = "Canada",Population = 35141542 ,WorldPercentage = 0.49 }, new Country { Id =37,Name = "Uganda",Population = 34131400 ,WorldPercentage = 0.48 }, new Country { Id =38,Name = "Iraq",Population = 33330000 ,WorldPercentage = 0.47 }, new Country { Id =39,Name = "Morocco",Population = 32973200 ,WorldPercentage = 0.46 }, new Country { Id =40,Name = "Peru",Population = 30475144 ,WorldPercentage = 0.43 }, new Country { Id =41,Name = "Malaysia",Population = 29747000 ,WorldPercentage = 0.42 }, new Country { Id =42,Name = "Uzbekistan",Population = 29559100 ,WorldPercentage = 0.42 }, new Country { Id =43,Name = "Saudi Arabia",Population = 29195895 ,WorldPercentage = 0.41 }, new Country { Id =44,Name = "Venezuela",Population = 28946101 ,WorldPercentage = 0.41 }, new Country { Id =45,Name = "Nepal",Population = 26494504 ,WorldPercentage = 0.37 }, new Country { Id =46,Name = "Afghanistan",Population = 25500100 ,WorldPercentage = 0.36 }, new Country { Id =47,Name = "North Korea",Population = 24895000 ,WorldPercentage = 0.35 }, new Country { Id =48,Name = "Ghana",Population = 24658823 ,WorldPercentage = 0.35 }, new Country { Id =49,Name = "Yemen",Population = 24527000 ,WorldPercentage = 0.35 }, new Country { Id =50,Name = "Mozambique",Population = 24366112 ,WorldPercentage = 0.34 }, new Country { Id =51,Name = "Taiwan[10]",Population = 23340136 ,WorldPercentage = 0.33 }, new Country { Id =52,Name = "Ivory Coast",Population = 23202000 ,WorldPercentage = 0.33 }, new Country { Id =53,Name = "Australia",Population = 23096296 ,WorldPercentage = 0.33 }, new Country { Id =54,Name = "Syria",Population = 21377000 ,WorldPercentage = 0.3 }, new Country { Id =55,Name = "Madagascar",Population = 20696070 ,WorldPercentage = 0.29 }, new Country { Id =56,Name = "Angola",Population = 20609294 ,WorldPercentage = 0.29 }, new Country { Id =57,Name = "Cameroon",Population = 20386799 ,WorldPercentage = 0.29 }, new Country { Id =58,Name = "Sri Lanka",Population = 20277597 ,WorldPercentage = 0.29 }, new Country { Id =59,Name = "Romania",Population = 20121641 ,WorldPercentage = 0.28 }, new Country { Id =60,Name = "Burkina Faso",Population = 17322796 ,WorldPercentage = 0.24 }, new Country { Id =61,Name = "Niger",Population = 17129076 ,WorldPercentage = 0.24 }, new Country { Id =62,Name = "Kazakhstan",Population = 17010000 ,WorldPercentage = 0.24 }, new Country { Id =63,Name = "Netherlands",Population = 16793700 ,WorldPercentage = 0.24 }, new Country { Id =64,Name = "Chile",Population = 16634603 ,WorldPercentage = 0.23 }, new Country { Id =65,Name = "Ecuador",Population = 15535800 ,WorldPercentage = 0.22 }, new Country { Id =66,Name = "Guatemala",Population = 15438384 ,WorldPercentage = 0.22 }, new Country { Id =67,Name = "Mali",Population = 15302000 ,WorldPercentage = 0.22 }, new Country { Id =68,Name = "Cambodia",Population = 15135000 ,WorldPercentage = 0.21 }, new Country { Id =69,Name = "Malawi",Population = 14388600 ,WorldPercentage = 0.2 }, new Country { Id =70,Name = "Senegal",Population = 13567338 ,WorldPercentage = 0.19 }, new Country { Id =71,Name = "Zambia",Population = 13092666 ,WorldPercentage = 0.18 }, new Country { Id =72,Name = "Zimbabwe",Population = 12973808 ,WorldPercentage = 0.18 }, new Country { Id =73,Name = "Chad",Population = 12825000 ,WorldPercentage = 0.18 }, new Country { Id =74,Name = "South Sudan",Population = 11296000 ,WorldPercentage = 0.16 }, new Country { Id =75,Name = "Cuba",Population = 11163934 ,WorldPercentage = 0.16 }, new Country { Id =76,Name = "Belgium",Population = 11156136 ,WorldPercentage = 0.16 }, new Country { Id =77,Name = "Guinea",Population = 10824200 ,WorldPercentage = 0.15 }, new Country { Id =78,Name = "Greece",Population = 10815197 ,WorldPercentage = 0.15 }, new Country { Id =79,Name = "Tunisia",Population = 10777500 ,WorldPercentage = 0.15 }, new Country { Id =80,Name = "Portugal",Population = 10562178 ,WorldPercentage = 0.15 }, new Country { Id =81,Name = "Rwanda",Population = 10537222 ,WorldPercentage = 0.15 }, new Country { Id =82,Name = "Czech Republic",Population = 10512800 ,WorldPercentage = 0.15 }, new Country { Id =83,Name = "Somalia[11]",Population = 10496000 ,WorldPercentage = 0.15 }, new Country { Id =84,Name = "Haiti",Population = 10413211 ,WorldPercentage = 0.15 }, new Country { Id =85,Name = "Bolivia",Population = 10389913 ,WorldPercentage = 0.15 }, new Country { Id =86,Name = "Benin",Population = 10323000 ,WorldPercentage = 0.15 }, new Country { Id =87,Name = "Burundi",Population = 10163000 ,WorldPercentage = 0.14 }, new Country { Id =88,Name = "Hungary",Population = 9906000 ,WorldPercentage = 0.14 }, new Country { Id =89,Name = "Sweden",Population = 9588569 ,WorldPercentage = 0.14 }, new Country { Id =90,Name = "Belarus",Population = 9458700 ,WorldPercentage = 0.13 }, new Country { Id =91,Name = "Dominican Republic",Population = 9445281 ,WorldPercentage = 0.13 }, new Country { Id =92,Name = "Azerbaijan",Population = 9235100 ,WorldPercentage = 0.13 }, new Country { Id =93,Name = "Austria",Population = 8464554 ,WorldPercentage = 0.12 }, new Country { Id =94,Name = "Honduras",Population = 8385072 ,WorldPercentage = 0.12 }, new Country { Id =95,Name = "United Arab Emirates",Population = 8264070 ,WorldPercentage = 0.12 }, new Country { Id =96,Name = "Switzerland",Population = 8058100 ,WorldPercentage = 0.11 }, new Country { Id =97,Name = "Israel",Population = 8034900 ,WorldPercentage = 0.11 }, new Country { Id =98,Name = "Tajikistan",Population = 8000000 ,WorldPercentage = 0.11 }, new Country { Id =99,Name = "Bulgaria",Population = 7282041 ,WorldPercentage = 0.1 }, new Country { Id =100,Name = "Serbia[12]",Population = 7241295 ,WorldPercentage = 0.1 }, new Country { Id =101,Name = "Hong Kong (China)",Population = 7173900 ,WorldPercentage = 0.1 }, new Country { Id =102,Name = "Papua New Guinea",Population = 7059653 ,WorldPercentage = 0.099 }, new Country { Id =103,Name = "Paraguay",Population = 6672631 ,WorldPercentage = 0.094 }, new Country { Id =104,Name = "Laos",Population = 6580800 ,WorldPercentage = 0.093 }, new Country { Id =105,Name = "Jordan",Population = 6467500 ,WorldPercentage = 0.09 }, new Country { Id =106,Name = "Eritrea",Population = 6333000 ,WorldPercentage = 0.089 }, new Country { Id =107,Name = "Libya",Population = 6202000 ,WorldPercentage = 0.087 }, new Country { Id =108,Name = "Togo",Population = 6191155 ,WorldPercentage = 0.087 }, new Country { Id =109,Name = "El Salvador",Population = 6183000 ,WorldPercentage = 0.087 }, new Country { Id =110,Name = "Sierra Leone",Population = 6092000 ,WorldPercentage = 0.086 }, new Country { Id =111,Name = "Nicaragua",Population = 6071045 ,WorldPercentage = 0.086 }, new Country { Id =112,Name = "Denmark",Population = 5605836 ,WorldPercentage = 0.079 }, new Country { Id =113,Name = "Kyrgyzstan",Population = 5551900 ,WorldPercentage = 0.078 }, new Country { Id =114,Name = "Finland",Population = 5434357 ,WorldPercentage = 0.077 }, new Country { Id =115,Name = "Slovakia",Population = 5410728 ,WorldPercentage = 0.076 }, new Country { Id =116,Name = "Singapore",Population = 5312400 ,WorldPercentage = 0.075 }, new Country { Id =117,Name = "Turkmenistan",Population = 5240000 ,WorldPercentage = 0.074 }, new Country { Id =118,Name = "Norway",Population = 5063709 ,WorldPercentage = 0.071 }, new Country { Id =119,Name = "Lebanon",Population = 4822000 ,WorldPercentage = 0.068 }, new Country { Id =120,Name = "Costa Rica",Population = 4667096 ,WorldPercentage = 0.066 }, new Country { Id =121,Name = "Central African Republic",Population = 4616000 ,WorldPercentage = 0.065 }, new Country { Id =122,Name = "Ireland",Population = 4585400 ,WorldPercentage = 0.065 }, new Country { Id =123,Name = "Georgia[13]",Population = 4483800 ,WorldPercentage = 0.063 }, new Country { Id =124,Name = "New Zealand",Population = 4471380 ,WorldPercentage = 0.063 }, new Country { Id =125,Name = "Republic of the Congo",Population = 4448000 ,WorldPercentage = 0.063 }, new Country { Id =126,Name = "Palestine[14]",Population = 4420549 ,WorldPercentage = 0.062 }, new Country { Id =127,Name = "Liberia",Population = 4294000 ,WorldPercentage = 0.06 }, new Country { Id =128,Name = "Croatia",Population = 4290612 ,WorldPercentage = 0.06 }, new Country { Id =129,Name = "Bosnia and Herzegovina",Population = 3839737 ,WorldPercentage = 0.054 }, new Country { Id =130,Name = "Oman",Population = 3831553 ,WorldPercentage = 0.054 }, new Country { Id =131,Name = "Puerto Rico (USA)",Population = 3667084 ,WorldPercentage = 0.052 }, new Country { Id =132,Name = "Kuwait",Population = 3582054 ,WorldPercentage = 0.05 }, new Country { Id =133,Name = "Moldova[15]",Population = 3559500 ,WorldPercentage = 0.05 }, new Country { Id =134,Name = "Mauritania",Population = 3461041 ,WorldPercentage = 0.049 }, new Country { Id =135,Name = "Panama",Population = 3405813 ,WorldPercentage = 0.048 }, new Country { Id =136,Name = "Uruguay",Population = 3286314 ,WorldPercentage = 0.046 }, new Country { Id =137,Name = "Armenia",Population = 3031200 ,WorldPercentage = 0.043 }, new Country { Id =138,Name = "Lithuania",Population = 2960733 ,WorldPercentage = 0.042 }, new Country { Id =139,Name = "Albania",Population = 2821977 ,WorldPercentage = 0.04 }, new Country { Id =140,Name = "Mongolia",Population = 2754685 ,WorldPercentage = 0.039 }, new Country { Id =141,Name = "Jamaica",Population = 2711476 ,WorldPercentage = 0.038 }, new Country { Id =142,Name = "Namibia",Population = 2113077 ,WorldPercentage = 0.03 }, new Country { Id =143,Name = "Lesotho",Population = 2074000 ,WorldPercentage = 0.029 }, new Country { Id =144,Name = "Slovenia",Population = 2060461 ,WorldPercentage = 0.029 }, new Country { Id =145,Name = "Macedonia",Population = 2059794 ,WorldPercentage = 0.029 }, new Country { Id =146,Name = "Botswana",Population = 2024904 ,WorldPercentage = 0.029 }, new Country { Id =147,Name = "Latvia",Population = 2013400 ,WorldPercentage = 0.028 }, new Country { Id =148,Name = "Qatar",Population = 1963124 ,WorldPercentage = 0.028 }, new Country { Id =149,Name = "Gambia",Population = 1849000 ,WorldPercentage = 0.026 }, new Country { Id =150,Name = "Guinea-Bissau",Population = 1704000 ,WorldPercentage = 0.024 }, new Country { Id =151,Name = "Gabon",Population = 1672000 ,WorldPercentage = 0.024 }, new Country { Id =152,Name = "Equatorial Guinea",Population = 1622000 ,WorldPercentage = 0.023 }, new Country { Id =153,Name = "Trinidad and Tobago",Population = 1328019 ,WorldPercentage = 0.019 }, new Country { Id =154,Name = "Estonia",Population = 1286540 ,WorldPercentage = 0.018 }, new Country { Id =155,Name = "Mauritius",Population = 1257900 ,WorldPercentage = 0.018 }, new Country { Id =156,Name = "Swaziland",Population = 1250000 ,WorldPercentage = 0.018 }, new Country { Id =157,Name = "Bahrain",Population = 1234571 ,WorldPercentage = 0.017 }, new Country { Id =158,Name = "Timor-Leste",Population = 1066409 ,WorldPercentage = 0.015 }, new Country { Id =159,Name = "Djibouti",Population = 864618 ,WorldPercentage = 0.012 }, new Country { Id =160,Name = "Cyprus[16]",Population = 862 ,WorldPercentage = 0.012 }, new Country { Id =161,Name = "Fiji",Population = 858038 ,WorldPercentage = 0.012 }, new Country { Id =162,Name = "Réunion (France)",Population = 821136 ,WorldPercentage = 0.012 }, new Country { Id =163,Name = "Guyana",Population = 784894 ,WorldPercentage = 0.011 }, new Country { Id =164,Name = "Bhutan",Population = 73674 ,WorldPercentage = 0.01 }, new Country { Id =165,Name = "Comoros",Population = 7243 ,WorldPercentage = 0.01 }, new Country { Id =166,Name = "Montenegro",Population = 620029 ,WorldPercentage = 0.0087 }, new Country { Id =167,Name = "Macau (China)",Population = 582 ,WorldPercentage = 0.0082 }, new Country { Id =168,Name = "Western Sahara[17]",Population = 567 ,WorldPercentage = 0.008 }, new Country { Id =169,Name = "Solomon Islands",Population = 561 ,WorldPercentage = 0.0079 }, new Country { Id =170,Name = "Luxembourg",Population = 537 ,WorldPercentage = 0.0076 }, new Country { Id =171,Name = "Suri,Name",Population = 534189 ,WorldPercentage = 0.0075 }, new Country { Id =172,Name = "Cape Verde",Population = 491875 ,WorldPercentage = 0.0069 }, new Country { Id =173,Name = "Malta",Population = 416055 ,WorldPercentage = 0.0059 }, new Country { Id =174,Name = "Guadeloupe (France)",Population = 403355 ,WorldPercentage = 0.0057 }, new Country { Id =175,Name = "Martinique (France)",Population = 394173 ,WorldPercentage = 0.0056 }, new Country { Id =176,Name = "Brunei",Population = 393162 ,WorldPercentage = 0.0055 }, new Country { Id =177,Name = "Bahamas",Population = 351461 ,WorldPercentage = 0.0049 }, new Country { Id =178,Name = "Iceland",Population = 32381 ,WorldPercentage = 0.0045 }, new Country { Id =179,Name = "Maldives",Population = 31728 ,WorldPercentage = 0.0045 }, new Country { Id =180,Name = "Belize",Population = 312971 ,WorldPercentage = 0.0044 }, new Country { Id =181,Name = "Barbados",Population = 2742 ,WorldPercentage = 0.0039 }, new Country { Id =182,Name = "French Polynesia (France)",Population = 26827 ,WorldPercentage = 0.0038 }, new Country { Id =183,Name = "Vanuatu",Population = 258213 ,WorldPercentage = 0.0036 }, new Country { Id =184,Name = "New Caledonia (France)",Population = 255651 ,WorldPercentage = 0.0036 }, new Country { Id =185,Name = "French Guiana (France)",Population = 22904 ,WorldPercentage = 0.0032 }, new Country { Id =186,Name = "Mayotte (France)",Population = 2126 ,WorldPercentage = 0.003 }, new Country { Id =187,Name = "Samoa",Population = 18782 ,WorldPercentage = 0.0026 }, new Country { Id =188,Name = "São Tomé and Príncipe",Population = 187356 ,WorldPercentage = 0.0026 }, new Country { Id =189,Name = "Saint Lucia",Population = 166526 ,WorldPercentage = 0.0023 }, new Country { Id =190,Name = "Guam (USA)",Population = 159358 ,WorldPercentage = 0.0022 }, new Country { Id =191,Name = "Curaçao (Netherlands)",Population = 150563 ,WorldPercentage = 0.0021 }, new Country { Id =192,Name = "Saint Vincent and the Grenadines",Population = 109 ,WorldPercentage = 0.0015 }, new Country { Id =193,Name = "United States Virgin Islands (USA)",Population = 106405 ,WorldPercentage = 0.0015 }, new Country { Id =194,Name = "Kiribati",Population = 104573 ,WorldPercentage = 0.0015 }, new Country { Id =195,Name = "Grenada",Population = 103328 ,WorldPercentage = 0.0015 }, new Country { Id =196,Name = "Tonga",Population = 103036 ,WorldPercentage = 0.0015 }, new Country { Id =197,Name = "Federated States of Micronesia",Population = 101823 ,WorldPercentage = 0.0014 }, new Country { Id =198,Name = "Aruba (Netherlands)",Population = 101484 ,WorldPercentage = 0.0014 }, new Country { Id =199,Name = "Jersey (UK)",Population = 97857 ,WorldPercentage = 0.0014 }, new Country { Id =200,Name = "Seychelles",Population = 90945 ,WorldPercentage = 0.0013 }, new Country { Id =201,Name = "Antigua and Barbuda",Population = 86295 ,WorldPercentage = 0.0012 }, new Country { Id =202,Name = "Isle of Man (UK)",Population = 84497 ,WorldPercentage = 0.0012 }, new Country { Id =203,Name = "Andorra",Population = 76246 ,WorldPercentage = 0.0011 }, new Country { Id =204,Name = "Dominica",Population = 71293 ,WorldPercentage = 0.001 }, new Country { Id =205,Name = "Bermuda (UK)",Population = 64237 ,WorldPercentage = 0.0009 }, new Country { Id =206,Name = "Guernsey (UK)",Population = 62431 ,WorldPercentage = 0.00088 }, new Country { Id =207,Name = "Greenland (Denmark)",Population = 5637 ,WorldPercentage = 0.00079 }, new Country { Id =208,Name = "Marshall Islands",Population = 55548 ,WorldPercentage = 0.00078 }, new Country { Id =209,Name = "American Samoa (USA)",Population = 55519 ,WorldPercentage = 0.00078 }, new Country { Id =210,Name = "Cayman Islands (UK)",Population = 55456 ,WorldPercentage = 0.00078 }, new Country { Id =211,Name = "Saint Kitts and Nevis",Population = 54 ,WorldPercentage = 0.00076 }, new Country { Id =212,Name = "Northern Mariana Islands (USA)",Population = 53883 ,WorldPercentage = 0.00076 }, new Country { Id =213,Name = "Faroe Islands (Denmark)",Population = 48244 ,WorldPercentage = 0.00068 }, new Country { Id =214,Name = "Sint Maarten (Netherlands)",Population = 37429 ,WorldPercentage = 0.00053 }, new Country { Id =215,Name = "Saint Martin (France)",Population = 36979 ,WorldPercentage = 0.00052 }, new Country { Id =216,Name = "Liechtenstein",Population = 36842 ,WorldPercentage = 0.00052 }, new Country { Id =217,Name = "Monaco",Population = 36136 ,WorldPercentage = 0.00051 }, new Country { Id =218,Name = "San Marino",Population = 32382 ,WorldPercentage = 0.00046 }, new Country { Id =219,Name = "Turks and Caicos Islands (UK)",Population = 31458 ,WorldPercentage = 0.00044 }, new Country { Id =220,Name = "Gibraltar (UK)",Population = 29752 ,WorldPercentage = 0.00042 }, new Country { Id =221,Name = "British Virgin Islands (UK)",Population = 29537 ,WorldPercentage = 0.00042 }, new Country { Id =222,Name = "Åland Islands (Finland)",Population = 28502 ,WorldPercentage = 0.0004 }, new Country { Id =223,Name = "Caribbean Netherlands (Netherlands)",Population = 21133 ,WorldPercentage = 0.0003 }, new Country { Id =224,Name = "Palau",Population = 2077 ,WorldPercentage = 0.00029 }, new Country { Id =225,Name = "Cook Islands (NZ)",Population = 14974 ,WorldPercentage = 0.00021 }, new Country { Id =226,Name = "Anguilla (UK)",Population = 13452 ,WorldPercentage = 0.00019 }, new Country { Id =227,Name = "Wallis and Futuna (France)",Population = 13152 ,WorldPercentage = 0.00019 }, new Country { Id =228,Name = "Tuvalu",Population = 11264 ,WorldPercentage = 0.00016 }, new Country { Id =229,Name = "Nauru",Population = 9945 ,WorldPercentage = 0.00014 }, new Country { Id =230,Name = "Saint Barthélemy (France)",Population = 8938 ,WorldPercentage = 0.00013 }, new Country { Id =231,Name = "Saint Pierre and Miquelon (France)",Population = 6081 ,WorldPercentage = 0.00006 }, new Country { Id =232,Name = "Montserrat (UK)",Population = 4922 ,WorldPercentage = 0.00005 }, new Country { Id =233,Name = "Saint Helena Ascension and Tristan da Cunha (UK)",Population = 4 ,WorldPercentage = 0.00004 }, new Country { Id =234,Name = "Svalbard and Jan Mayen (Norway)",Population = 2655 ,WorldPercentage = 0.00003 }, new Country { Id =235,Name = "Falkland Islands (UK)",Population = 2563 ,WorldPercentage = 0.00003 }, new Country { Id =236,Name = "Norfolk Island (Australia)",Population = 2302 ,WorldPercentage = 0.00002 }, new Country { Id =237,Name = "Christmas Island (Australia)",Population = 2072 ,WorldPercentage = 0.00002 }, new Country { Id =238,Name = "Niue (NZ)",Population = 1613 ,WorldPercentage = 0.00002 }, new Country { Id =239,Name = "Tokelau (NZ)",Population = 1411 ,WorldPercentage = 0.00001 }, new Country { Id =240,Name = " Vatican City",Population = 800 ,WorldPercentage = 0.00001 }, new Country { Id =241,Name = "Cocos (Keeling) Islands (Australia)",Population = 550 ,WorldPercentage = 0.000001 }, new Country { Id =242,Name = "Pitcairn Islands (UK)",Population = 66 ,WorldPercentage = 0.0000000 } }; } } }As you can see, we must tag the service with the ScriptService and tag the WebMethod with the ScriptMethod attribute, specifying the JSON responseformat. We also need to add some to our web.config:
<?xml version="1.0"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <webServices> <protocols> <add name="HttpGet" /> <add name="HttpPost"/> </protocols> </webServices> </system.web> </configuration>We also need some GUI here to test out the TypeAhead control:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head< <title></title> <link href="Content/bootstrap.css" rel="stylesheet" /> <link href="Content/bootstrap-responsive.css" rel="stylesheet" /> </head> <body> <div class="row"> <div class="span12"> <input type="text" id="tbCountries" /> </div class="row"> <script src="Scripts/jquery-2.0.3.js"></script> <script src="Scripts/bootstrap.js"></script> <script src="Scripts/underscore.js"></script> <script type="text/javascript"> $(function () { var countries; $("#tbCountries").typeahead({ source: function (query, process) { return $.ajax({ type: "POST", url: 'http://localhost:52523/CountryService.asmx/GetCountries', data: JSON.stringify({query: query }), contentType: "application/json; charset=utf-8", dataType: "json", success: function (data) { countries = data.d; var countryIds = _.map(countries, function(c){ return c.Id; }); return process(countryIds); } }); }, matcher: function (item) { return true; }, sorter: function (items) { return items; }, highlighter: function (id) { var country = _.find(countries, function (c) { return c.Id == id; }); return country.Name + " Population: " + country.Population; }, updater: function (id) { var country = _.find(countries, function (c) { return c.Id == id; }); return country.Name; } }); }); </script> </body> </html>To sum up, to make the TypeAhead control work in my example, I had to implement stub implementations of matcher, sorter, highlighter (must be implemented such that the custom Country object instances return in JSON object is visible, this is a display template in other words) and updater (will update the text field with the autocomplete / TypeAhead feature). Source also sets a global variable countries here for simplicity, and continues processing the ids, you can see that we use Underscore.Js here for the array processing. The highlighter and updater method uses the find method of Underscore.js to find the relevant country. The end result is a functioning auto complete example for the Bootstrap TypeAhead control that uses a remote JSON service. I am not sure why the data.d variable was returned from the service. I did not specify "data.d" anywhere in my code, but this is where I could find the returned JSON data. It is not very elegant to set the countries to a global variable, we could of course encapsulate this better using a "provider" to encapsulate the data for encapsulation and consistency protection. But the key point of this article was to help others getting started with the TypeAhead control in Bootstrap with remote JSON calls returning objects with properties and not just simple string arrays. I find the Bootstrap documentation from Twitter as good, but not exemplary. It is sometimes hard to find good programming examples on the different controls. The end result is an autocomplete control that looks nice and can display compound data:
Wednesday, 24 July 2013
Structuring your Javascript code - Revealing prototype pattern and namespaces
Creating Spaghetti-code in Javascript is not difficult. There are no clear pattern in Javascript for encapsulation for novice developers of Javascript, unless you really are familiar with what is possible with Javascript. Most developers learn Javascript in an ad-hoc manner, just adding functions to a Javascript file and keep on adding code to their monolithic design until they realize that a pattern must be used to structure the Javascript code.
Of course, today, with the huge focus on Javascript, there are multiple patterns to follow and coding Javascript is a more mature topic that it used to be.
There are four designs which we can choose when we structure our code (possible more):
- Module pattern
- Prototype pattern
- Revealing module pattern
- Revealing prototype pattern
var AppUtils = AppUtils || {}; AppUtils.LocalStorageUtility = function(storageKeys, sourceElements, feedbackElement) { this.storageKeys = storageKeys; this.sourceElements = sourceElements; this.feedbackElement = feedbackElement; }; AppUtils.LocalStorageUtility.prototype = (function () { var storeSettings = function (thisObj) { for (var i = 0; i < thisObj.storageKeys.length; i++) { var storageKey = thisObj.storageKeys[i]; var elementName = thisObj.sourceElements[i]; localStorage.setItem(storageKey, $("#" + elementName).val()); } $("#" + thisObj.feedbackElement).text("Local setting saved!"); }, loadSettings = function (thisObj) { for (var i = 0; i < thisObj.storageKeys.length; i++) { var storageKey = thisObj.storageKeys[i]; var elementName = thisObj.sourceElements[i]; $("#" + elementName).val(localStorage.getItem(storageKey)); } $("#" + thisObj.feedbackElement).text("Local setting loaded!"); }, clearSettings = function () { localStorage.clear(); }, hasLocalStorage = function () { return typeof (Storage) !== "undefined"; } return { StoreSettings: storeSettings, LoadSettings: loadSettings, ClearSettings: clearSettings, HasLocalStorage: hasLocalStorage }; })();The consumer of this class-like structure in Javascript will provide two string arrays which is the local storage key names and the elements to read and store local storage values from. I do not do any checking here that the two arrays are the same length or that they are of the correct type (arrays preferably with string of course). This is what TypeScript version of this code would specify. Remember, TypeScript is much easier to use than manually creating class like structs like we do here, but the concepts are anyways important to understand. In the revealing prototype pattern we return an object literal and the public part is exposed here, i.e. the revealing prototype. In addition, we call it revealing because the prototype is self invoking. The private parts are what is outside of the return statement above. We point to the private methods, and this is fine, and we alias them to a capitalized version to let the user call into the private methods. If we want public fields We also need a GUI for the Javascript code here:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Localstorage demo</title> </head> <script src="Scripts/jquery-2.0.3.min.js"></script> <script src="Scripts/LocalStorageUtility.js"></script> <script type="text/javascript"> $(document).ready(function () { var ls = new AppUtils.LocalStorageUtility(["Name", "City"], ["tbName", "tbCity"], "lblFeedback"); if (ls.HasLocalStorage) { $("#lblFeedback").text("Local storage is enabled!"); } else { $("#lblFeedback").text("Local storage is disabled!"); } $("#btnLoadSettings").click(function () { ls.LoadSettings(ls); }); $("#btnSaveSettings").click(function () { ls.StoreSettings(ls); }); $("#btnClearSettings").click(ls.ClearSettings); }); </script> <body> <table> <tr> <td>Name</td><td><input id="tbName" type="text" /></td> <td>City</td><td><input id="tbCity" type="text" /></td> </tr> </table> <button id="btnLoadSettings" type="button">Load settings</button> <button id="btnSaveSettings" type="button">Save settings</button> <button id="btnClearSettings" type="button">Clear settings</button> <p id="lblFeedback" style="color: darkgreen">ready</p> </body> </html>As you can see, with this pattern we must keep track of the this pointer in the prototype definition. There are alternatives, such as bind, and call, but I wanted to keep the example clear. In the jQuery load method above we define click handlers that pass in the instance of the LocalStorageUtility, which we have instantiated, such that the prototype gets the correct instance. As you perhaps know, the prototype must bind to the correct this pointer, and here we explicitly pass it in (i.e. the instance of LocalStorageUtility). Finally, note that we define a namespace here with the "or empty object literal" technique. This allows us to hide the entire class from the global namespace and improve or encapsulation even more. If you have comments to the code above, please let me know. I have worked some with Javascript of course, but I am still learning a lot about it.
Subscribe to:
Posts (Atom)