How to lazy load scripts in AngularJS app?

Some time ago I was having a problem with JS loading from partials in an AngularJS app and today I decided to share my solution with you. The problem is that when web page is loaded, javascript libs in partials are not being loaded. You have to force them to load. You can do this with some imagination, a directive and some jQuery (the last one is optional). 🙂

app.directive('script', [function() {
  function load_script(src, identifier) {		
    var s = document.createElement('script');
    s.type = 'text/javascript';
		
    if(identifier !== undefined) {
      // if element with some identifier exists, remove it                         
      jQuery('script[data-identifier="' + identifier + '"]').remove(); 
      // add an identifier to the new script object so we can remove it if needed
      s.setAttribute('data-identifier', identifier);
    }	
		     
    if(src !== undefined) {		
      s.src = src;
    }
    else {
      var code = elem.text();
      s.text = code;
    }

    document.body.appendChild(s);
  }

  return {
    restrict: 'E',
    scope: false,
    link: function(scope, elem, attr) {
      if (attr.type=='text/javascript-lazy') {
        load_script(elem.attr('src'), elem.attr('data-identifier'));
        elem.remove();
      }
    }
  };
}]);

When partial is loaded the directive happens for each sript object in that partial. If script is of type text/javascript-lazy, load_script function is called. This function creates new element of type text/javascript and adds src or code of lazy element to the new element. All elements with specific (custom) identifier are then removed from DOM and new element is appended to body. When appended to body, script gets forcefully loaded.

Error when opening explorer: “Server execution failed error.”

Recently I had a problem with explorer on Windows 7 – when coworker tried to open explorer when not on local network, “Server execution failed” error happened. I was suspecting that the problem might be connected to mapped disks which were not accessible, when not on local network, but wasn’t sure how this could lead to such an obscure error. After some heavy duty browsing I found a solution: the problem was in “Launch folder windows in a separate process” setting. This setting can be found somewhere in Folder Options window and it should be unchecked on Windows 7. By default this setting is not checked, because it can eat more RAM and can slow down your computer. But it also says that it could increase Windows stability (when error in one process occurs other processes are not affected). But obviously there is a bug in Windows 7, because everything stops working when not in domain. 🙂 In Windows 8.1 I didn’t detect this problem. Well, problem solved – yipikaye motherf****r. 🙂

VirtualBox is missing x64 version of guest OSs

I was migrating x64 Ubuntu VM from one VirtualBox to another (on different computer). The procedure is as simple as it can be: you copy files from one machine to another and than add new machine. When you are asked to configure hard disk, you choose “Use an existing virtual hard disk file” and set the location to those files and that’s pretty everything you have to do. But I had a problem – when creating new VM there was no x64 version to choose. First thing that came to my mind was that probably virtualization was not enabled in BIOS. I checked but that was not it. Secondly I checked if my host machine has x64 OS installed (I was 99,99% sure, but sicher ist sicher, as Germans say :)). Lastly I found this nice blogpost about the same problem I had. It said that Hyper-V should be disabled, because if it is not, VirtualBox cannot access some virtualization stuff. You can turn off Hyper-V in “Turn Windows features on or off” in Control Panel -> Programs -> Programs and Features:

 

Windows Features

Windows Features

And that exactly was my problem. After I turned Hyper-V off the selection of x64 OSs was possible. In case of the same problem you have to:

  1. Check if your host OS is x64.
  2. Check if your computer supports virtualization – if it does, enable it in BIOS.
  3. Check if you have Hyper-V enabled – if it is, disable it.

If those conditions are met it should work.

NLog tutorial

Hi, folks!

Today I am going to show you how to set logging of your project with minimum effort using NLog. NLog is a simple to use logging platform with good documentation, tutorials and examples and also available source code. You can install package using NuGet selecting NLog.Config, which will also install it’s dependencies (NLog and NLog.Schema). Installation adds NLog.dll to our project refrences, NLog.config for configuration and NLog.xsd (which won’t be needed in our case).

Initial configuration is simple and allows basic internal logging. But our goal is to set new log file each day with date in it’s name and email notifications in case of warnings and (fatal) errors.

<variable name="logFilePath" value="Logs\NLog.${shortdate}.log" />

Inside of variable tag we declared log file path with short date in it’s name. With date in it’s name new log file will be created every day. After that we have to create a file target and mail target:

<targets>
  <target name="logfile" 
          xsi:type="File"
          fileName="${logFilePath}"
          layout="${longdate}   LEVEL=${level:upperCase=true}: ${message}${newline} (${stacktrace}) ${exception:format=tostring}"
          keepFileOpen="true" />

  <target name="mail" 
          xsi:type="Mail"
          smtpServer="my.smtp.server.com"
          smtpPort="port"
          smtpUserName="user.name"
          smtpPassword="password"
          subject="${machinename} - My subject string (${shortdate:format=dd. MM. yyyy})"
          from="address.to.send.from@mail.com"
          to="address.to.send.to@mail.com"
          layout="${longdate}   LEVEL=${uppercase:${level}},   LOCATION=${callsite:className=true:includeSourcePath=true:methodName=true},               
                  MESSAGE=${message}${newline} EXCEPTION=${exception:format=tostring,StackTrace}${newline}" />
</targets>

Now we have two targets. First named “logfile” will add new entries to our log. Eeach entry will consist from long date, level and message. After message there will be new line with stacktrace and exception message. Second named “mail” will send email to address.to.send.to@mail.com. Entry will be quite similar as previous with added location (class name and method name). But we have a problem – each log entry will be sent separately! If we want to send all entries in one single email, we have to wrap our “mail” target with target of type BufferingWrapper:

<target name="mailbuffer" xsi:type="BufferingWrapper" slidingTimeout="false" bufferSize="100" flushTimeout="-1">
  <target name="mail" 
          xsi:type="Mail"
          smtpServer="my.smtp.server.com"
          smtpPort="port"
          smtpUserName="user.name"
          smtpPassword="password"
          subject="${machinename} - My subject string (${shortdate:format=dd. MM. yyyy})"
          from="address.to.send.from@mail.com"
          to="address.to.send.to@mail.com"
          layout="${longdate}   LEVEL=${uppercase:${level}},   LOCATION=${callsite:className=true:includeSourcePath=true:methodName=true},               
                  MESSAGE=${message}${newline} EXCEPTION=${exception:format=tostring,StackTrace}${newline}" />
</target>

Problem solved. 🙂 Now we will get all entries inside of a single email. After that we have to set rules, which will tell NLog which level of entries must be written to which target.

<rules>
  <logger name="*" minlevel="Info" writeTo="logfile" />
  <logger name="*" minlevel="Warn" writeTo="mailbuffer"/>
</rules>

With those rules two rules we told NLog to log to file everything with level equal or higher to Info and to send an email of everything with level equal or higher to warning. Out logger is now configured. After that we have to add namespace to each class we want to log (using NLog;). We also have to initialize our logger. After that we can start to create logs:

class Program
{
  private static Logger logger = LogManager.GetCurrentClassLogger();

  static void Main(string[] args)
  {
    logger.Info("This is the first line of Main method.");

    try
    {
      throw new Exception("This is one badass exception. :)");
    }
    catch (Exception ex)
    {
      logger.Error(ex, "It seems the exception happened. :(");
    }

    logger.Warn("This is your last warning!");
    logger.Fatal("And this is fatal error...");
  }
}

Here you can download config file and short code snippet: NLog config and short code snippet. Before using you have to configure SMTP parameters (you can find all possible parameters in documentation or you can check this example (both on GitHub)).

MS Word field functions: OR function

At work I am using hybrid of Interop and OpenXml to create some reports on client side. These reports are created from MS Word templates. Because MS Word is our preferred text editor I also can use MS Word functions for some operations. And today I was having fun using function OR. MS Word functions are very simple to use, but if you make one itsy bitsy tiny mistake in syntax some generic error appears which tells you less than nothing (something like “!Syntax Error ,,“).

I needed to check inside of IF field if document property has equal value as one of two other values and then do something. In pseudo code it would look something like this:

if(value is equal value1 OR value is equal value2)
  then do something
else 
  do something else

The thing is that OR function works perfectly with numbers, but not with strings. With numbers it looks like:

{ =OR(1 = 1; 1 = 5) }

This would return 1, as expected (warning: you must use semicolon , not comma). But with strings it would return syntax error. With strings you need to use field COMPARE:

{ =OR( { COMPARE "value" = "value1" }; { COMPARE "value" = "value2" }) }

COMPARE returns 1 if condition is met or returns 0 if condition is not met. If one of COMPAREs returns 1, condition inside OR is met – OR returns 1. In other case it returns 0. IF field would look like this:

{ IF { =OR( { COMPARE "value" = "value1" }; { COMPARE "value" = "value2" })} = 1 "when TRUE, use this" "when FALSE, use this"}

As I said, very simple to use, but when you have a very long sausage of text with IF fields and nested functions every small mistake becomes great pain in the arse (pardon my language).

How to do detect when ngRepeat is finished and after that do something?

Today I am going to show you a simple solution of the problem, presented in the title of this post. You can detect end of ngRepeat with a directive:

app.directive('onLastRepeat', function() {
  return function(scope, element, attrs) {
    if(scope.$last)
      setTimeout(function() {
        scope.$emit('onRepeatLast');
      }, 0);
  }
});

As you can see I firstly check if last item is being processed. If this is true, I set timeout for 0ms. This is an ugly hack, but it works. The problem is that AngularJS has a process named $digest which asynchronously inserts data into template using ngRepeat directive and we don’t know when the process of data insertion is finished. After that we call our function inside of parent scope with $emit and there the magic happens 🙂 (in my case I reseted some widths and heights).

This solution only works if you have one ngRepeat inside of a controller. In other cases you could decide which function to call based on special attributes or something.

Have a nice day! 😉

How to export collection of data to Xlsx file in C# using ClosedXML

ClosedXML is a fine library for exporting data to Excel. It is very simple to use and fast enough for smaller sets of data. But it also has some cons:

1.) It uses OpenXML – that is not a con by itself, but the problem appears when you want to save file as Xls. XML concept of Office files was presented with Office 2007 and is not compatible with previous versions of Office. But if you really need Xls, you can create Excel file using ClosedXML and than open and save it as Xls using Interop. That is only possible if you are exporting data on client side with installed PIA and Office. It is strongly advised against using Interop on server side due to the fact that Office is client side application!
2.) It is very slow for big datasets (more than few 10 thousand rows). For exporting big datasets you should use OpenXML (maybe I will talk about that in one of my next posts).
3.) On project’s CodePlex site it is written (quote): “ClosedXML makes it easier for developers to create Excel 2007/2010 files.”. But I believe Excel 2013 and newer should also open files generated with ClosedXML (haven’t tested it though).

Firstly you have to add reference to ClosedXML. You can do that using NuGet Package Manager or you can download ClosedXML DLL from CodePlex and add a reference to your project.

NuGet

NuGet Package Manager

You also need to add a namespace to your CS file (using ClosedXML.Excel;).

Using ClosedXML you can set specific cell values, you can insert data from DataTable or from some other collection. In this post I will show you how to create generic function, which inserts data to Excel from IEnumerable of objects. I will pass column titles in separate List of string arrays.


Info: I could probably set column titles using descriptors on object properties – something like this:

[MyAttribute("First property")]
public string FstProperty
{
  get {....}
  set {....}
}

But for the sake of the example I won’t complicate – I will pass the titles to the function in a separate List.


Our function should look something like this:

public byte[] ExportToExcel<T>(IEnumerable<T> data, string worksheetTitle, List<string[]> titles)
{  
  var wb = new XLWorkbook(); //create workbook
  var ws = wb.Worksheets.Add(worksheetTitle); //add worksheet to workbook

  ws.Cell(1, 1).InsertData(titles); //insert titles to first row
            
  if (data != null && data.Count() > 0)
  {
    //insert data to from second row on
    ws.Cell(2, 1).InsertData(data); 
  }

  //save file to memory stream and return it as byte array
  using (var ms = new MemoryStream())
  {
    wb.SaveAs(ms);

    return ms.ToArray();
  }
}

And that is it! If we wish to add some special styling to our heading row, we have to modify our code snippet a little:

var rangeTitle = ws.Cell(1, 1).InsertData(titles); //insert titles to first row
rangeTitle.AddToNamed("Titles");

var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true; //font must be bold
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; //align text to center
titlesStyle.Fill.BackgroundColor = XLColor.Red; //background must be red

wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; //attach style to the range

We can also adjust column width to maximum content width using function AdjustToContents right before we save the file:

ws.Columns().AdjustToContents();

How to detect device type with JavaScript?

There is a simple way how to detect if user is accessing your web site using mobile device (like phone, tablet or their hybrid brother phablet) or stationary device. You can detect it using navigator object. We will neglect small players on the mobile market and focus only on the main players. Example:

var mobile = {
  Android: function() {
    return navigator.userAgent.match(/Android/i);
  },
  BlackBerry: function() {
    return navigator.userAgent.match(/BlackBerry/i);
  },
  iOS: function() {
    return navigator.userAgent.match(/iPhone|iPad|iPod/i);
  },
  Opera: function() {
    return navigator.userAgent.match(/Opera Mini/i);
  },
  Windows: function() {
    return navigator.userAgent.match(/IEMobile/i);
  },
  any: function() {
    return (mobile.Android() || mobile.BlackBerry() || mobile.iOS() || mobile.Opera() || mobile.Windows());
  }
};

If you are interested if user is accessing your site using BlackBerry (obscure example, I know – why would anyone care about BlackBerry 🙂 ) you can do it like this:

var isandroid = function() {
  mobileArray = mobile.BlackBerry();
  if(typeof mobileArray !== 'undefined' && mobileArray != null && mobileArray.length > 0) {
    return true;
  }
  return false;
}

Or shortly:

var isandroid = function() {
  mobileArray = mobile.BlackBerry();
  return (typeof mobileArray !== 'undefined' && mobileArray != null && mobileArray.length > 0);
}

How to detect user’s device size (xs, sm, md or lg) using Bootstrap?

Today I am going to show you a simple “hack” – how to detect device size using Bootstrap (and a short snippet of jQuery). In order to make it work you obviously have to import Bootstrap and jQuery JS and CSS files.

Firstly you create a div with four containing divs:

<div id="users-device-size">
  <div id="xs" class="visible-xs"></div>
  <div id="sm" class="visible-sm"></div>
  <div id="md" class="visible-md"></div>
  <div id="lg" class="visible-lg"></div>
</div>

Those divs will of course never show on your site. Their sole purpose is to tell you the device size. After that you have to add short function to your JS file (or to your JavaScript section inside of your HTML):

function getBootstrapDeviceSize() {
  return $('#users-device-size').find('div:visible').first().attr('id');
}

This function will retrieve current visible div’s id, which tells you the device size in Bootstrap way (xs – extra small device [<768px], sm – small device [>=768px], md – medium device [>=992px] and lg – large device [>=1200px]).

Push C# (Razor) objects to JavaScript array of objects

If you wonder, how to push C# objects to a JavaScript array in ASP.NET View using Razor, you came to the right place. 🙂 In this post I will tell you, how I achieved it.

Lets say I have a collection List<Category>. Object Category consists of parameters id, category name and list of subcategories (also of type Category):

public class Category {
  public int Id { get; set; }

  public string Name { get; set; }

  public List<Category> Subcategories { get; set; }
}

List<Category> is also my strongly typed View’s Model. My goal is to achieve Javascript array like this: [{“id”:…, “name”:”…”, “subcategories”:[{“id”:…, “name”:”…”}]},{…}]. In order to create objects like that, I must firstly create collection of anonymous objects and serialize it to string.

@{
  //serializer initialization
  System.Web.Script.JavascriptSerializer js = new System.Web.Script.JavascriptSerializer();

  var data = Model.Select(c => new {
    id = c.Id,
    name = c.Name,
      subcategories = c.Subcategories.Select(cc => new {
      id = cc.Id,
      name = cc.Name
    })
  });

  //serialize collection of anonymous objects
  string arrStr = js.Serialize(data);
}

Now I have to inject this string to JavaScript:

var categories = JSON.parse('@arrStr'.replace(/&quot;/g, '"'));

As you can see, I injected string using Razor. I also used replace function in order to replace all &quot; occurances with " (double quotes).

Warning: JavaScript replace function replaces only first occurrance of searched pattern. In order to replace all occurrances I had to use regular expression with flag g, which is used to perform a global match.

The last thing to do is to call JSON.parse, which converts properly processed string into JavaScript object. Now we have JavaScript array of desired objects, which can be used further in our JavaScript functions/events.