Tuesday, February 18, 2014

Email discussions

.. they just don’t work

We have all been there. Probably multiple times as well. The endless sinkhole of discussions running on email. Someone send you an email or replies to an email you send or you are just one of the innocent bystanders that are in the CC-list. That email then triggers yet another reply from someone, for some trivial reason maybe. But then the avalanche starts. Avalanches usually start with just one small ball of snow. The effects of an avalanche can be disastrous and so are the effects of an email avalanche
Do not play with dynamite in the mountains
It is really easy to react to some incorrect or plain stupid email. Or something that just annoys the heck out of you. A manager happily announcing to a customer that we will be shipping next week and joyfully CC-ed you along in the mail.
Enough to make your head explode and maybe even go ALL CAPS on him.
It’s so easy to fall into the trap. It is so easy to try and start a discussion via email by simply pressing reply and start typing. Often without really letting your brains kick in. With a healthy strong cup loaded with cafeine you can really get the words and long sentences flowing. After banging prose for several minutes you sit back and admire your work and know for 100% sure that this will solve all misunderstandings and will re-eductaed all those on the list of addressees.
But in reality you hold a match very close to the fuse of a stick of dynamite.
When you hit send you light the fuse.
Boom!
You return to work and after some time you check your inbox and disaster has struck. An avalanche of replies to your solve-it-all reply. People have written long epistels back explaining things again and how everything was agreed upon and what was not agreed upon and that they weren’t there when it was agreed and that the situation has changed and soon some will GO ALL CAPS ON EVERYONE INCLUDING YOU. YES YOU!
You see that more and more people have gotten involved and that more people are receiving the mail and oh yes just like an avalanche it has gone rapidly downhill.
Maybe someone is accusing you or anyone else already of being a Nazi.
Stop right there
Yes, it is already too late. So stop replying. It is simply impossible to get switch on sanity again in this situation. Stop it.
The only solution right now is get face to face with the people really involved in the matter. Which you should have done right from the beginning.
Single out the most relevant people in the matter and send a meeting request. Get face to face or if itis just one or two people get on the phone or Skype or Hangouts or whatever you prefer. Quit the email avalanche which is making things worse with each reply.
Better stil..
Better is to prevent the entire escalation. Whenever you find yourself writing a big email reply and you feel your heart beating stronger than normal and you drink your coffee faster than normal, please do not hit that send button.
Cut and paste the text you have written in a document and turn the email in a simple meeting request. You can use the document as your notes for the meeting.
The words “Let’s meet and discuss” are so much more powerful than any piece of well written prose no matter how long.

Thursday, January 30, 2014

Trying to get back into blogging

.. and it's a struggle

I have been planning and  planning to write more on this blog. I have been trying to at least write a single piece again on this blog. It has been way, way too long since the last post. It's been some eight months now. Bummer, that's bad.
Looking back over the years I can see that my blog has been drying out. Nothing comes out of the well anymore. That is not good.
Let's take action
Blogging has been on my list, always. It still is. Simple as that. But so have many other activities. Including work. And hobbies. It has been difficult to find the right balance to do everything I want to do. I have scraped some subjects in Google Docs but have never gotten around to work these out into proper blog posts. There is more needed for that than just a few more lines of text.
But anyway I need to take some action. I need to look for and find the time to pick things up again.
And I have found a tool: Lift. It’s a tool for achieving goals and literally lifting you up again. There is also an app in the Play Store and on iTunes.
Daily motivation
Pick a plan in Lift and you get a plan divided up into simple steps that you can take daily and slowly and surely achieve your goal. Having done a task simple check it and optionally add a comment. Other Lift users can give you props for the achievement and can also start a discussion to help you or others achieve their goals.
This is truly a form of daily motivation.
One of my goals is writing
I picked one of the goals to get me back into writing more regularly and so far it has helped me a lot. This blogpost is one of the products to prove it.
There are other plans that I have chosen and that help me to get more grip on my goals in life. I find it a great tool to help me help myself with some help from unknown friends.

Friday, May 10, 2013

The benefits and horrors of third party controls

.. or how I learned to love the bomb

Don't reinvent the wheel
Don't reinvent the wheel (Photo credit: Wiertz Sébastien)
When developing web applications that should handle all sorts of data and potentially lots of it you might end up in building all kinds of controls for displaying and editing that data. Before you know it you are creating clever pieces of html and even cleverer pieces of backend code to produce that html and an added thick layer of Javascript sauce to make all this work together.
And that brings you into inventing another even cleverer mouse trap or re-inventing the wheel. And that mouse trap or wheel has to be cross browser and multi-functional, databound et cetera et cetera et cetera.
Me, I don't like re-inventing wheels, that has been done so many times already.
So, clear the stage for third party controls
Telerik
Telerik (Photo credit: martin.linkov)
One these re-inventors of wheels is Telerik. They have various suites of controls that make the life of developers easier. They have menu controls and date(time) edit boxes with calenders and grid controls that you can easily use in your applications. Set some options and bind them to your data and there you go.
I must admit that there is a bit of a learning curve, but with the help of some good examples on the demo site and the ever helpful support desk of Telerik you get everything up and running in no time.

As easy as .. whatever is easy.

But it is not all easy
When you start out all things go smoothly and then you find that on your page with a couple of nested grids you end up with many, many save buttons. As each (sub)grid has one. And thus starts the quest to use only one button to rule them all.

So here is our SaveAll Javascript function.

function SaveAll(button) {
    showLoader();

    $.ajaxSetup({
        async: false,
        cache: false
    });

    $('.t-grid').each(function (index) {
        var grid = $(this).data('tGrid');
        grid.submitChanges();
    });

    $('form').not('.t-edit-form').each(function (index) {
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serializeArray(),
            type: $(this).attr('method')
        });
    });

    hideLoader();

    $.ajaxSetup({
        async: true,
        cache: true
    });

    return false;
}
 What happens?

  • We start by showing a loader div (with spinning gif )on the page and so making it impossible for the user to do something while everything gets saved.
  • Then we make sure that all consequent ajax calls are asynchronous as we would otherwise run ito trouble.
  • Next we loop through all the grids submit all changes for each grid.
  • Then we loop through all the forms (excluding the forms with class "t-edit-form", these are are for inline editing in the grids) and we submit the forms through ajax.
  • Finally, we hide the loader div again.
There you are. That was not that difficult.
I have omitted checking for validation errors and what to do when everything is saved. Maybe we need to move to another page?
Well, it almost works, can I go home now?
Almost working is never enough for testers and clients. But is probably even more annoying for me as a developer.
We discovered a weird behavior in our grids. Because we wanted to unclutter our pages we also removed the delete buttons in the grids for removing records and added a column at the beginning of each row with a checkbox so the user can select multiple rows. Then using a delete button in the toolbar you can delete the rows in one go.
Great!
And then we discovered that the value of the first data column was not saved using our button. Other columns worked perfectly. The data was simply not in the request to the server.

After a lot of reaearch, Googling, trial and error
It was clear that the solution was just around the corner. The addition of the checkboxes was the cause. That was easily established, but a solution was more difficult.
Browsing through the Telerik Javascript code and the DOM I tried to add this to part where we are saving the grids.
    $('.t-grid').each(function (index) {
        var grid = $(this).data('tGrid');
        // save any editing rows first
        $(this).find(".t-grid-edit-row").each(function () {
            grid.updateRow(this);
        });
        grid.submitChanges();
    });
That would simply take the rows that are in editing mode and update the data. But no, this tried to update it directly on the server and that would involve additional writing specific server side code to handle those requests.
Close and still no cigar.
The problem was that the edited column was somehow not persisted in the client side data. So that was what  needed to be made sure. Persist all data in client side memory before submitting the grid changes.
But how?
More Googling
Trying evermore results from Google brought me to Paul Reynold's blog. In his post "enhanced batch editing using telerik extensions for asp .net mvc grid control" he explains a lot. Not all of it was relevant for my solutions, but I found a bit of code that helped to fix my problem. Thank you, Paul.

        $('.t-grid').each(function (index) {
        var grid = $(this).data('tGrid');
        // save any editing cells first
        $(grid.element).find(".t-grid-edit-cell").each(function () {
            grid.saveCell(this);
        });
        grid.submitChanges();
    });

I tried to update an entire row (which resulted in a server call) and the solution is to save each cell and these get persisted client side. Just what I wanted.

Lessons learned
I learned a couple of things in this quest:
  1. Third party controls are great for making life easier, because all normal functionality is there and tested.
  2. Third party controls make things really difficult at times when you want to do something slightly out of the box.
  3. Solutions can be found by hunting through multiple search results and reading all the way through these.
  4. Never give up.

Enhanced by Zemanta

Thursday, April 04, 2013

Multiple submit event handlers on Ajax forms in a wizard style application

.. this was driving me crazy!

I was working on an application (ASP.NET, MVC3) in which I had to implement a wizard like piece of functioality. You know of the 'Next-Next-Previous-Next-Finish'-type.
To make things nice for the user I used AJAX. To make things simpler for me I have one control that is loaded and depending on the page that the user is currently on I load different controls into the form. I return a PartialView and that replaces part
That is all not too complicated and works like a charm.
Uhm, no it doesn't
The first page shows up. I hit the submit button and the page went by smoothly and I could see the ControllerAction being hit. Great! I just increased the page counter and onwards to the next page. The next page arrives in the browser and I hit the submit button again.
Uh oh! Things go wrong now. My ControllerAction is hit twice! Worse even on the next page, it gets hit three times! Oh my, oh my.
Inspection of the problem
Looking at the html closer I quickly discovered that the form had multiple event handlers tied to the 'submit' event. This was the cause of the forms being submitted multiple times.
How come these multiple event handlers then?
Apparently the jQuery Ajax (jquery.unobtrusive-ajax.min.js) has some trouble with the Ajax replacing a part of the DOM and then reattaches some of the existing 'submit' Event handlers. The extra event handler on each page is thus explained.
Root cause is that the AJAX call reloads part of the DOM including the FORM-element and not the content of the FORM and thus somehow the old submit eventhandlers are not cleared. Hence, multiple event handlers.
Okay, so let me Google that for you
Or Bing it. Or Yahoo it.
Many people have encountered similar problems. But none quite the same as mine or at least the provided solutions did not solve my problem. And so I experimented with many solutions and all of them failed.
Thought about somehow preventing all the event handlers but the first to fire. Or just to delete all event handlers except the first. Couldn't find a good way of doing that.

Back to the drawing board
Completely rebuilding the Views and PartialViews to refresh the DOM inside the FORM element was not an option. So that was ruled out.
In the end we decided to go a slightly other way. We decided to step away from a normal submit button as that triggers (all) the submit event handler(s) of the FORM and makes us lose control of the mess we got in. We created a simple button that calls a JavaScript function that does the submit of the FORM through jQuery AJAX. This way we never ever call the submit event handler of the form.
Problem solved.
Please find below the simple JavaScript function that does all the clever stuff.
function onSingleSubmitButtonClicked(button)
{
  var frm = $('form');
  var data =  frm.serialize();
  var url = frm.attr('action');
  var method = frm.attr('data-ajax-method');
  var target = frm.attr('data-ajax-update');
  $.ajax({
    type: method,
    url: url,
    data: data,
    beforeSend: function() {showLoader();},
    complete: function(response) {$(target).html(response.responseText);hideLoader();}
  });
}
We attach this function to the click event of input[type=button] and that is all. Below a step by step run through.
  1. The function finds the one form on our page. When you have more forms on a page you may have to pass an id or when the button is inside the form find the parent of the button of type FORM.
  2. Next we serialize all the input fields from the form.
  3. We then grab the action attribute from the form and use that as the url to post the data to.
  4. We read the post or get method from the appropriate method.
  5. The update target where we will put the response from the Ajax call into the DOM.
  6. Then we make the Ajax call. The serialized data is included and we also show a loader overlay before we send the request and hide it again after the call is complete.
Probably some optimization is possible, but that is a simple solution to the weird problem. Just take things in your own hands. You can't always trust the browser or jQuery do things the way you would expect them to do.

Monday, December 05, 2011

Creating a master-detail grid with Telerik ASP.NET MVC controls

.. with a twist

Right now I am part of a team that is building a big HR related application. Maybe it's better to call it an HR suite. When finished it can be used to do performance reviews, 360s, development plans and the like with this application. It will be big and the customer plans to conwuer the world wit it. Well, at least Europe.
Architecture
For this applications some choices were made with regards to the archictecture.
First decision: we use ASP.NET MVC3. Why? Well, main reason is that this is becoming some sort of standard way of developing web applications using .NET. Is it the best way? Is the easiest way? Is it the only way? No. No. No. It is one of the possible frameworks to use. And because we expect to get more people on board we pick a framework that more people use over own framework.
The second choice we use Entity Framework and with that the Code-First option. This was a first for us and it takes a bit getting used to not have to write any clever stored procedures.
Database access is separated from the web application and is handled through web services. Making it so much easier to later decide to share the load over more machines.
Finally, a decision that was already made by the client, we should Telerik Components were possible.
Getting back to the subject
Ok, let's get back to what this really about. Implementing a master-detail grid with a Telerik component.
So why a blog post on this? Telerik has an extensive demo site and has a demo on a master detail grid. Yes, they have, but we want to do it slightly different.
To be totally honest, we think that the standard way of ASP.NET MVC3 is in some repect a step back. Done the simple way it means that a lot of the logic is done in the aspx page. It comes close again to classic ASP. Mixing markup with C# code and ending up with not optimal code in both of these areas. We are great believers in Object Oriented approach and we seek to reuse as much code as possible and want to kae our controls as clever as it needs to be.
Beacuse the target market is Europe we have to cater for different languages and we have solved that problem in a base control and derive all controls from that.
In the aspx page, we only want to load a custom user control that we build up completely in code. No, we did not go down the Razor path.
So yes, we have private controls and we override the CreateChildControls() method. We do the binding in the OnPreRender() and finaly in the Render(writer) method we make sure the correct markup is being produced.
Brining in the Telerik controls
The above approach also means that we cannot use the Telerik components as private controls. It is not possible to instantiate the components. So in comes the Telerik.Web.Mvc.UI.Fluent namespace and a variety of Builders. With these builders it is relatively easy to build the component. When we have set all properties we want and bound the data we then use the method ToHtmlString() to get the markup produced by the component and assign that outcome to a LiteralControl. After that we let the rendering go on in the nromal way.
This way we can use our own OO-based control hierarchy and still use the powerful Telerik Components. We do not wish to completely build (and test) a sortable, pageable, editable grid control.
Now for the Master-Detail grid
In one of our Use Cases we needed a Master-Detail grid. No problem the Telerik Grid component supports that as shown in the demo.
The problems was how to get that done in our approach using the Builders. We did not find any real examples of this.
Telerik support team
So then we got into the normal horror of sending a question to the support desk. Normally it takes some days to get a reply and then you need a couple of messages to get them to understand your question and then explain why you want to have it done the way you are doing it. Before finnaly, you get into the struggle of getting an answer that really works.
But no wait!
The Telerik support is different!
They reply within 24 hours as advertised. I even had an answer with ten minutes on one of my questions.
And they quickly understood what I was trying to and gave me enough info to solve my problem with a workday. And being probably in different time zones I think that is a wondeful achievement! Many thanks for that!
As a big thank you I wil explain what the final solution was.
The problem
We want show a grid with all the departments of a company and we want to click open (expand) a department to show a list of it's employees.
Nothing too special.
The solution
Our data model is pretty simple:
Department
Properties
  • Guid DepartmentId
  • string Name
  • List Employees
Employee
Properties
  • Guid EmployeeId
  • string FirstName
  • string FirstName
Our control has a LiteralControl that will be the placeholder for the markup produced by the GridBuilders. Also there is a private field that holds a list of Departments with the corresponding Employees.
Let's start using the GriDBuilder to first create the Master part.
GridBuilder gridBuilder = HtmlHelperExtension.Telerik(viewPage.Html).Grid();
            gridBuilder.Name("MasterDetailGrid")
               .DataBinding(dataBinding => dataBinding
                   .Server())
               .BindTo(departments)
               .Columns(columns =>
               {
                   columns.Bound(d => d.Name).Title("Department");
               });
This is the normal way to use the GridBuilder to create a Telerik Grid.
The Detail part of the Grid is itself also a Grid, so we can use teh same approach.
GridBuilder employeeGridBuilder = HtmlHelperExtension.Telerik(viewPage.Html).Grid()
   .DataBinding(dataBinding => dataBinding
   .Server())
   .Name("Detail")
   .Columns(columns =>
   {
      columns.Bound(d => d.FirstName).Title("FirstName");
      columns.Bound(d => d.LastName).Title("LastName");
});
Note that I have not bound this Deatil Grid to anything yet. That is something we need to do as part of the master Grid. To do so we need the DetailView method of the Grid.
gridBuilder.DetailView(dep => dep.Template(d =>
   {
      employeeGridBuilder.BindTo(d.Employees);
   }));
This makes the Employee bound to the Employees of the Department that is in the row of the Master part of the Grid. Now, spit out the markup and put it in the LiteralControl.
literalControl.Text = gridBuilder.ToHtmlString();
But nothing shows up for the Employees. We ca expand a Department row and there is some white space, but no Employees.
The final step
Something inside my head said that I had to use ToHtmlString() on the detail Grid as well. But I could not find the correct syntax. So back to the Supprt Team at Telerik and an answer came very quick. It was simple as the following:
gridBuilder.DetailView(dep => dep.Template(d =>
   {
      employeeGridBuilder.BindTo(d.Employees);
      return employeeGridBuilder.ToHtmlString();
   }));
This makes the detail Grid appear and now we can move forward again full speed!
Thansk again!
Not only does Telerik produce great components, they can be used in various ways. Even the not so standard way we want to use them. Telerik also has a super Support Team is both fast and correct in their answers.
If you ever run ito a problem with Telerik components they will surely be able to help you out in no time!
Enhanced by Zemanta