Monday, June 19, 2017

Working with XML and LocalStorage in JavaScript & JQuery

We talked about XML in our previous post about how to generate and keep in LocalStorage for client side access.
Lets look at how we can deal with it from the browser using JavaScript and JQuery.
 $(document).ready(function(){  
   var swXmlDocumentAttrib = localStorage.getItem("swXmlDocumentAttrib");  
   var swXmlDocumentAttribJS = $.parseXML(swXmlDocumentAttrib);  
   $(swXmlDocumentAttribJS)      
        .find('AttributeItem')      
        .each(function(index, element){  
         var field = $(element)        
         var idConfigAttribute = field.find('idConfigAttribute').text()  
         var lbConfigAttribute = field.find('lbConfigAttribute').text()  
   });// end of swXmlDocumentAttribJS loop   
 });// end of document ready  

The LocalStorage is ready once the document is loaded.

localStorage.getItem
This will retrieve the stored item with the given key.

$.parseXML
Here we used JQuery to make it east to parse string to XML

AttributeItem
This is the item name

each(function(index, element)
Each item has it's elements with index. This is a loop that iterates until it finishes the document reach the end of items.

var idConfigAttribute = field.find('idConfigAttribute').text()
This get's the each element with matching key.

Like so, we can iterate through the XML and process it as needed. If further clarification is needed, post it via comments. I am happy to write back.

Happy coding...

Sunday, June 18, 2017

Creating XmlDocument in C# & Store in LocalStorage in JS

Mostly we work with data in code behind when developing web applications. Sometimes we do postbacks and reload pages. Sometimes we use AJAX and do partial page updates. Sometimes we don't like both of those but need to keep so much data in client side to access easily even if the network is disconnected.
Today we will see how we overcome this by creating XML document and store it in LocalStorage so that it can be accessed by client side scripts.

1. Create your own XML Document in Code Behind.
 XmlDocument xmlDocumentAttrib = new XmlDocument();  
       XmlDeclaration xmlDeclarationAttrib = xmlDocumentAttrib.CreateXmlDeclaration("1.0", "UTF-8", null);  
       XmlElement rootAttrib = xmlDocumentAttrib.DocumentElement;  
       xmlDocumentAttrib.InsertBefore(xmlDeclarationAttrib, rootAttrib);  
       XmlElement ConfigAttributeList = xmlDocumentAttrib.CreateElement(string.Empty, "ConfigAttributeList", string.Empty);  
       xmlDocumentAttrib.AppendChild(ConfigAttributeList);  
       foreach (var pa in this._Product.ConfigAttributeList)  
       {  
         if (attributeNotInList.Where(x => x.idConfigAttribute == pa.idConfigAttribute).FirstOrDefault() != null)  
           continue;  
         XmlElement AttributeItem = xmlDocumentAttrib.CreateElement(string.Empty, "AttributeItem", string.Empty);  
         ConfigAttributeList.AppendChild(AttributeItem);  
         XmlElement idConfig = xmlDocumentAttrib.CreateElement(string.Empty, "idConfigAttribute", string.Empty);  
         XmlElement lbConfig = xmlDocumentAttrib.CreateElement(string.Empty, "lbConfigAttribute", string.Empty);  
         XmlText idConfigAttributeText = xmlDocumentAttrib.CreateTextNode(pa.idConfigAttribute.ToString());  
         XmlText lbConfigAttributeText = xmlDocumentAttrib.CreateTextNode(pa.lbConfigAttribute);  
         idConfig.AppendChild(idConfigAttributeText);  
         lbConfig.AppendChild(lbConfigAttributeText);  
         AttributeItem.AppendChild(idConfig);  
         AttributeItem.AppendChild(lbConfig);  
 }  

this._Product
This is an object that holds the list of properties.
this._Product.ConfigAttributeList
This contains list of attributes that each one has it's ID and Name.

This peace of code will generate an XML document like below;


2. Now we need to append this to a string.
We use StringWriter and XmlWriter for this.
 var swXmlDocumentAttrib = new StringWriter();  
 using (var xmlTextWriter = XmlWriter.Create(swXmlDocumentAttrib))  
       {  
         xmlDocumentAttrib.WriteTo(xmlTextWriter);  
         xmlTextWriter.Flush();  
         swXmlDocumentAttrib.GetStringBuilder().ToString();  
       }  

The swXmlDocumentAttrib is containing the xml document created above.

3. Inject this to the client side using code behind.
 StringBuilder sbLocalStorage = new StringBuilder();  
 sbLocalStorage.Append("<script>");  
 sbLocalStorage.Append("if (typeof(Storage) !== \"undefined\") {");  
 sbLocalStorage.Append("localStorage.setItem('swXmlDocumentAttrib', '" + swXmlDocumentAttrib + "');");  
 sbLocalStorage.Append("} else { alert('does not support local storage'); }");  
 sbLocalStorage.Append("</script>");  
 Page.ClientScript.RegisterStartupScript(this.GetType(), "productLocalStorage", sbLocalStorage.ToString());  

This will alert if the browser doesn't support for LocalStorage.

localStorage.setItem
The setItem add the string value to the LocalStorage of the browser.

Page.ClientScript.RegisterStartupScript
RegisterStartupScript will register the script in HTML page.

In your client side scripting you can get this stored xml using getItem.

We will discuss further details of how we can deal with such XML from client side in upcoming posts.

Happy Coding...!

Tuesday, June 13, 2017

Working with JavaScript with C# - Single Quote Issue Fixed

Hi Guys,

After some busy days, Here I come again with a small problem I had and of course with the solution too.

Sometimes we have to build JavaScript within our code to inject it to the page. A declared variable for a string in JavaScript can't have a single quote because it gives an error at the end when we need it. This mostly happens with multilingual websites such as in french they use single quote a lot.

Look at the below c# method that can replace the single quote with JS friendly string.


In the same class we can use this as ;


It's simple. Next let's see how we use this to build a XML from C# and store it in JS Local Storage.

Happy coding...

Tuesday, May 30, 2017

Helping Hand for Flood Relief 2017

Helping Hand for Flood Relief 2017

Dear Friends,
It's time to get together and provide a helping hand to our flood affected brothers and sisters. 
We have planned to collect goods & money for this donation drive. You could contribute by supplying  
  • Baby products (milk powder, soap, diapers etc....), 
  • Clothes (New ones or used ones in very good condition), 
  • Sanitary napkins, 
  • Women's Underwear, 
  • Medicine (OTC) and dry rations 
(Items are ordered in the priority).

We are trying to find a transportation channel and we expect your cooperation on that too. 
Otherwise we'll hand over the goods to Rathmalana Air Force Camp or one of the donation programs arranged by radio channels.

Our collection center is at "Level 10, Access Towers, Union Place, Colombo 02

If it's hard for you to visit? us, 
you may transfer your donations to a bank account 
so that we purchases the above items from a wholesale agent in Fort. 
(Please request bank details.) 

We do share the updates and maintain 100% transparency of your donations.


Let's give them a hand.
The best way to find yourself is to lose yourself in the service of others. Mahatma Gandhi

Tuesday, January 17, 2017

Saving Bulk Records in SQL Server

Database operations are costly. Especially if we deal with big number of data. Sometimes even timeout may happen in your application.

There is a better way we can handle this from code level which we can use SqlBulkCopy class to insert bulk data into SQL Server Tables.

You can read more about SqlBulkCopy class here.

The "WriteToServer" method can be used with a DataTable filled with all your data from the application.

Lets get to work;
 DataTable dtFiles = new DataTable();  
 string _conStringRead = string.Empty;  
 string _strPathToScan = string.Empty;  
 _strPathToScan = ConfigurationManager.AppSettings["folderPath"]; // reading folderpath  
 _conStringRead = ConfigurationManager.ConnectionStrings["Read"].ConnectionString; //reading connection string from App.Config file  
 dtFiles.Columns.Add("lbFileName"); // Adding Column to DataTable  
 foreach (var file in Directory.EnumerateFiles(_strPathToScan, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToList()) // Get files in folder  
           { //append them to DataTable  
             var toInsert = dtFiles.NewRow();  
             toInsert["lbFileName"] = file;  
             dtFiles.Rows.Add(toInsert);  
           }  
 using (SqlConnection sqlConRead = new SqlConnection(_conStringRead))  
           {//Bulk Insert  
              sqlConRead.Open();  
             var bulkCopy = new SqlBulkCopy(sqlConRead);  
             bulkCopy.DestinationTableName = "Your Table Name";  
             bulkCopy.ColumnMappings.Add("lbFileName", "lbFileName");  
             bulkCopy.WriteToServer(dtFiles);  
           }  

So the App.Config file looks like below;

 <connectionStrings>  
   <add name="Read" connectionString="Data Source=xxxx;Initial Catalog=DB;Persist Security Info=True;User ID=development;Password=development; Current Language=French;" providerName="System.Data.SqlClient"/>    
 </connectionStrings>  
 <appSettings>    
   <add key="folderPath" value="X:\FolderPath\"/>    
 </appSettings>  

Here I insert the list of files in the folder to a given table by matching it's columns with the DataTable columns at once. This is a great solution for dealing with large amount of data.

Try and you will enjoy this.

Happy Coding...

Wednesday, November 23, 2016

Phone numbers - make it easy to read with JQuery

When entering and displaying phone numbers, sometimes its annoying to read all numbers together. Because it may be hard to read. If we can add a space in between few numbers to make it masked, it would be ideal for the user to read it quickly.

It is easy with JQuery. Look at the below function.

 function addSpace(control, flPaste) {    
   if (flPaste == false) {  
     var txtVal = $("#" + control).val();  
     if (txtVal.length == 2) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 5) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 8) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 11) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
   }  
   else if (flPaste == true) {  
     setTimeout(function () {  
       var txtVal = $("#" + control).val();  
       var txtValNew = "";  
       for (var i = 0, len = txtVal.length; i < len; i++) {  
         txtValNew += txtVal[i];  
         if (i == 1) {  
           txtValNew += " ";  
         }  
         else if (i == 3) {  
           txtValNew += " ";  
         }  
         else if (i == 5) {  
           txtValNew += " ";  
         }  
         else if (i == 7) {  
           txtValNew += " ";  
         }  
       }  
       $("#" + control).val(txtValNew);  
     }, 100);  
   }  
 }  


This can be applied to the textboxes by binding the events below;

 //for typing  
 $("#txtadPhone").keyup(function () {  
     addSpace("txtadPhone",false);  
   });    
 // for pasting  
   $("#txtadPhone").bind('paste', function (e) {  
     addSpace("txtadPhone", true);  
   });  

So the textbox will look like this;


So you will have 14 characters for a 10 digit numbers here. Therefore you may need to cater it in the database or else you need to remove the space using a method in the codebehind. For example,


 public static string removeSpace(string str)  
     {  
       if (str == null)  
         return null;  
       var vVal = string.Empty;  
       foreach (var v in str.ToCharArray())  
       {  
         if ((int)v == 32)  
           continue;  
         vVal += v.ToString();  
       }  
       return vVal;  
     }  

This is a static method that remove the space. However, you may not need to apply this but if there is a restriction in the DB side, you can always use this.

Happy Coding... :)

Tuesday, November 1, 2016

Solved : ASP.Net form submit and page expire when navigate back

Each page we create in ASP.Net has at least one form. Those form can be submitted in many ways. Either a postback from a server control or an html button can submit a form using JavaScript.

Look at the below image.




















The page has expired due to a submission and user tries to navigate backward which the server needs the page to be submitted. Simulating the issue in a live environment would be like below;

1. You can have a filtering criteria as this.
2. The "Search" is an anchor tag that has a onClick event bind to it.




3. When user tick on each of the check boxes, the id values are stored in a hidden field and on "javascript:submitForm()" function the page (form) is resubmitted to the server to filter the product list. So the "submitForm" function looks like below;

 <input id="hdnFilterCriteria" name="hdnFilterCriteria" value="4339435,4339434,4339427," type="hidden">  


4. This causes the page to post back with the filtering criteria appended to the hidden field.
5. After the postback, if the user move in to another page and hit browser back button to see the previous page, the following error will display due to the document expiration.


















It is time to see how we can avoid this error. It is not that hard and all you need is to think outside the box. Simply avoid page postbacks / submissions. In this case we uses JQuery AJAX. Let's focus about the solution.

1. Create a class with the all properties included.
 public class SearchAttribute  
 {  
   public string idAttribute { get; set; }    
   public string strFilterCriteria { get; set; }    
   // You can have all the attributes as properties here  
 }  

2. Use the Cache / Session to maintain the search history. You can use Ajax to update the cache. See the web method below;

   [System.Web.Script.Services.ScriptMethod]  
   [WebMethod(EnableSession = true)]  
   public string catProductHiddenValueCaching(int idSearch,string idAttribute, string strFilterCriteria)  
   {  
     var cacheName = HttpContext.Current.Session.SessionID.ToString() + "_SearchAttribute";  
     Dictionary<int, SearchAttribute> searchHistory = new Dictionary<int, SearchAttribute>();  
     if (idSearch > 1)  
     {        
       var cacheSA = HttpContext.Current.Cache.Get(cacheName);  
       if (cacheSA != null)  
         searchHistory = (Dictionary<int, SearchAttribute>)cacheSA;  
     }  
     SearchAttribute sa = new SearchAttribute()  
     {  
       idAttribute = idAttribute,  
       strFilterCriteria = strFilterCriteria  
     };  
     searchHistory.Add(idSearch, sa);  
     HttpContext.Current.Cache.Insert(cacheName, searchHistory);      
     JavaScriptSerializer js = new JavaScriptSerializer() { MaxJsonLength = Int32.MaxValue };  
     return js.Serialize(idSearch.ToString());  
   }  

  • Use Session ID + Cache name to avoid conflicts with shared cache location.
  • Use Dictionary to store search attribute instances of each search
  • Use the Cache.Insert to overwrite the existing cache value.

3. Inside the onclick event of the "Search" button, call this function asynchronously.

     function submitForm(){  
          try{  
         var j = jQuery.noConflict();  
         var inSearch = getParameterByName('inSearch'); // to track the no of search histories  
         if(inSearch){  
             inSearch = parseInt(inSearch);  
             inSearch++;  
           }  
         else  
           inSearch = 1;  
         var urlAjax = 'http://'+window.location.hostname+'/your_service.asmx/setFilderValueCache'; // get the service path with full URL  
         var idAttribute = j("#idAttribute").val();  
         var strFilterCriteria = j("#strFilterCriteria").val();  
         var objPara = {  
           idSearch:inSearch,  
           idAttribute: idAttribute,  
           strFilterCriteria: strFilterCriteria  
         };  
         j.ajax({  
           type: "POST",  
           url: urlAjax,  
           data: JSON.stringify(objPara),  
           contentType: "application/json; charset=utf-8",  
           dataType: "json",  
           success: function (msg) {  
             var ret = JSON.parse(msg.d);     
             var url = window.location.href;    
             var newUrl = queryStringUrlReplacement(url, 'inSearch', inSearch); // append the inSearch as a query string parameter.  
             window.location.href = newUrl;               
           },  
           error: function (err) {  
             alert(err.responseText);  
           }  
         });  
          }catch(e){}  
     }  

  • idSearch is used to update the query string and reload the page with query string parameter.

 function queryStringUrlReplacement(url, param, value)   
     {  
       var re = new RegExp("[\\?&]" + param + "=([^&#]*)"), match = re.exec(url), delimiter, newString;  
       if (match === null) {          
         var hasQuestionMark = /\?/.test(url);   
         delimiter = hasQuestionMark ? "&" : "?";  
         newString = url + delimiter + param + "=" + value;  
       } else {  
         delimiter = match[0].charAt(0);  
         newString = url.replace(re, delimiter + param + "=" + value);  
       }  
       return newString;  
     }  

4. When "Search" is clicked, the relevant cache object is accessed and passed the search criteria to it's parameters inside "Page Load"

 protected void Page_Load(object sender, EventArgs e)  
   {      
     string strFilterCriteria= ((Request.Form["strFilterCriteria"] ?? Request.QueryString["strFilterCriteria"]) ?? string.Empty);  
     string inSearch = Request.QueryString["inSearch"];  
     if (!string.IsNullOrEmpty(inSearch))  
     {  
       int iSearch = int.Parse(inSearch);  
       var cacheName = HttpContext.Current.Session.SessionID.ToString() + "_SearchAttribute";  
       Dictionary<int, SearchAttribute> searchHistory = new Dictionary<int, SearchAttribute>();  
       var cacheSA = HttpContext.Current.Cache.Get(cacheName);  
       if (cacheSA != null)  
         searchHistory = (Dictionary<int, SearchAttribute>)cacheSA;  
       if (searchHistory.Count > 0)  
       {  
         SearchAttribute sa = searchHistory[iSearch];  
         if (sa == null)  
           return;  
         if (!string.IsNullOrEmpty(sa.idAttribute))  
           idAttribute = int.Parse(sa.idAttribute);  
         strFilterCriteria= sa.strFilterCriteria;               
       }  
     }  
    // Use 'strFilterCriteria' variable to store the currently filtered id list and pass it to the relevant method to get the result.  
 }  


Now each time the "Search" is clicked, it stores the search criteria in the cache and reload with inSearch as a query string parameter. There is no page postback and no document expiration happens. Each time it loads a fresh copy with the idSearch in the query string as "?inSearch=1" or "?inSearch=2" ..etc. The number increases on each Click to Search button. Navigate backward will retrieve the relevant result from the cached dictionary.

So, it is easy. Just a simple solution. :)

Happy coding... :)