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]).