Thursday, 7 December 2017

Angular2 - Material Autocomplete and remote filtering data

Problem:

The last couple of days I was struggling to get the Angular2 Material Autocomplete to work with our api. I wanted the autocomplete options remotely filtered. Several solutions I found, worked with a (cached) list of items. So all items were first retrieved and after that, those were filtered by the autocomplete. This is ok if the full list is a short list, but not for long(er) lists.
Well, after searching and trying a lot, and for a long time (also together with my colleague Frank den Outer, special thanks to him!) and struggling with promises and observables, we found the solution and it is actually really simple (at least, it looks like that).

The data is coming from an server api and is accessed by the function getCompanies(search: string) from a company.service.ts (which is injected in the user component). This function returns an Observable.

Solution


On the user.component.html I used:












On the related user.component.ts, you the following pieces of code:



The 'debounceTime(300)' is used to prevent a api-call on each keyup event.
In our case, if no value is entered in the input box, the api returns the first 20 entries of the list of companies.
The main difference with other solutions (where a (cached) list on client side is used) is the .subscribe() part. In other solutions you will find a .map call, something like this:


where allCompanies is a fetched list with all companies. This .map function will not respond correctly with the customSearchFunction if the result is retrieved directly from a api.

But I really prefer the 'subscribe' solution, because you get directly your data filtered from you api and you don't need to preload all data and store it local. And it is less code as well ;)

Happy programming!

Tuesday, 31 January 2017

ASP.NET MVC $.ajax error handling

Problem:
I tried inside my (classic) MVC web application to show custom error(message)s when using $.ajax(). Several options tried, like:
return new HttpStatusCodeResult(401, "Custom Error Message 1");
but no luck. On some weird way, the error was not cached inside my $.ajax().error(...)

Solution:
I wrote a custom extension method on Controller level:
public static class ControllerExtensions
{
 public static ContentResult CustomError(this Controller ctl, int statuscode, string message)
 {
  ctl.Response.ContentType = "application/json";
  ctl.Response.StatusCode = statuscode;
  ctl.Response.Write(message);
  return new ContentResult();
 }
}

So, inside my controller actions, I can just call:
 return this.CustomError(400, myerrormessage);

On the view I now can just do the following:
$.ajax({
 // do you stuff here
 success: function (response) {
  // do success response actions
 },
 error: function (data, textStatus, jqXHR) {
  // the 'data' contains data.responseText, which is the 'myerrormessage' entered into the extension method
  // 'data' contains data.status, which is the statuscode entered into the extension method (400)
  toastr["error"]("Error(s) during loading group list:
" + data.responseText);
 }


Well, that's it for me. It might be useful for you :)

Thursday, 8 December 2016

NHibernate.StaleStateException : Unexpected row count

Situation:
Console application with NHibernate and a MySQL database. The application read several (large) files with data and process it to the database. For several reasons, all entries that are read from the files needs to be compared with existing entries in the database. New one's should be added, existing entries should be updated (if needed) and entries that does not exist anymore in the import files needs to be update (property set to inactive = true). So far so good.

Problem:
I ran several times into the NHibernate.StaleStateException with the unexpected row count message.
I changed from session.save() and session.flush() to a transaction with commit way of dealing with the objects. I also started to use Parallel.ForEach when processing the read entries for performance reasons. During testing (with smaller files) everything went well. Till I started to read files from over 15Mb (I got even files of > 60Mb). That was the moment that those NHibernate.StaleStateException's were fired.

Solution:
After searching a lot and reading a lot of flushing, saves, transactions and versions I still got not the correct answer for my problem.
However, I've found the solution. It was not really an Nhibernate issue, but a MySQL issue. I had to change a few database parameters:
  • innodb_buffer_pool_size. I changed the default value of 8M to 512M. This speed up the entire process a lot. On production server(s) this value can be set higher as well (depending on the available RAM of the machine).
  • innodb_log_file_size. I changed this one to 128M
  • max_allowed_packet. If this value is to small, NHibernate will fire those StaleStateExceptions if you are processing such amount of data(files). So I changed it to 100M
Those parameter you can find in the my.ini file of MySQL (location of my file is at C:\ProgramData\MySQL\MySQL Server 5.7\my.ini). More info of those parameters can be found in the my.ini file.
After changing those parameters, you need to restart the MySQL service.

For me, those 3 db-parameters did the trick.

Monday, 18 July 2016

Tilde key not working in VMware fusion

Problem:

Today I ran into a small but frustrating problem. The tilde key (~) didn’t work on my VMware fusion machine.

Situation:

I run Windows 10 in VMware fusion on a Macbook Pro. I searched a bit around the internet but didn’t find the right solution. By the way, my keyboard layout is US, so the ‘~’ key should be on the key just left from the ‘1’.
Some trial and error with keyboard combination with CTRL, ALT, Winkey etc didn’t work out. Strangely enough, I got it to work on the command prompt by pressing SHIFT+ALT+`, but that didn’t work on e.g. Notepad++ and Visual Studio.

Solution:

I found out that you can fix that in the settings of the VM itself. You don’t need to shutdown you VM. Just do the following:

  • Open the Settings of you VM
  • Click on ‘Keyboard & Mouse’
  • Just under the list of Profiles, you will see a gear wheel button which you can expand.
  • Click in the gear wheel popup menu on the option ‘Edit profile’
  • In the popup window, click on the tab ‘Key Mapping’ and unselect the option ‘Enable Language Specific Key Mappings’. This will do the trick (a least for me).
  • Close all settings windows, return to your (running) VM and voila, the ‘~’ will work back again.

I assume that this will work on other Windows versions on VM's as well. Just give it a try.

Happy programming!

Monday, 9 May 2016

MS Access and LINQPad

Situation:

As most developers, using LINQ queries saves a lot of time and works absolutely great. For me one of the greatest tools for quick and easy querying data and testing code is LINQPad. However, sometimes you get a project where the (backend)database is MS Access (yes, it still happens, even in 2016 :) ).

Problem:

And there starts the problem. There is no MS Access driver for LINQPad, at least no free one.

Solution:

But we are lucky, LINQPad provides a way to create custom drivers. So one of my colleagues, Frank den Outer, made a driver for MS Access to enable you to connect to *.mdb and *.accdb and allows you to query on data from LINQPad on MS Access database (even with password protected ones). So the credits of this post goes to him!

See the screenshots below:






Our company, DNM, offers you this driver for free. You can download is here: MSAccessDataContextDriver.lpx. After the download, start LINQPad, and when creating a new connection, you can easily add the downloaded driver.

Happy programming!

Android Screen Recording on Mac

Scenario:

For one of the apps we are developing, I needed to make a screen cast of some strange behaviour inside the app on Android. But how can you record screen activity from a Android device on Mac OS X?

Solution:

After a little bit of searching, the solution was actually pretty simple. You need to do the following:

1. Install the Android Tool
Download the Android Tool from GitHub (please check out the GitHub project page to keep track of updates and news). The download gave you a zip file. Unzip the file and place the tool inside the application folder on your Mac.

2. Prepare your Android device
In order to be able to establish a successful communication between your Android device and the Mac, you have to enable the 'Developer Options'. Inside the Developer Options, you need to enable the 'USB Debugging' feature. If you like, you can also enable the feature that shows your touch actions on the screen.

3. Launch the Android Tool
Start the Android Tool (note: you may get the warning that the app is from an unidentified developer). Once the app is opened, simply plug in your Android device

4. Start Recording
The tool is as easy as it can be:
Click on the camera button to take a screenshot or click on the camcorder button for a screen recording. When done with screen recording, press the red square (stop) button which appeared on the camcorder button place. You can record as long as you wish :).
The files created will be stored in an 'AndroidTool' folder on your desktop.

Note: You won't be able to record audio with this tool and neither you can take screenshots of record videos of protected videos.

Last tip: once you have your screencast video file, put it through Handbrake. This will decreases you file size a lot (e.g. 8.3 MB to 2.4 MB)

Monday, 1 June 2015

Fix slow boot-up on Mac

Problem
Recently I ran into a frustrating issue on my new MacBook Pro Retina (with 512MB SSD). The time of (re)boot took a lot of time, holding the screen black for a long time.
I checked several tips, but it was not due to old hardware (...), also not that my disk was full (more than 50% free space).
I searched a lot and found a solution that works actually pretty well and seems really stupid to me (a kind of Microsoft-type-solution-when-something-unexpectedly-stop-working-well).

Solution
What you need to do is to go to System Preferences, and selecting Startup Disk. Then, select your primary disk, and hit ‘Restart’ from the same window.


That's all! It worked for me pretty well!
By the way, it seems that this happened after the update of OS X 10.10.3.