Preventing multiple calls on button in Angular

If you wish to prevent your users from clicking on a button multiple times in a row, for example if the button triggers some animation that needs to complete before being triggered again, you can use debouncing.

Debouncing is a form of rate limiting in which a function isn’t called until it hasn’t been called again for a certain amount of time. That is to say, if the debouncing time is 200ms, as long as you keep calling the function and those calls are within a 200ms window of each other, the function won’t get called.

You could use a button’s [disabled] attribute and setTimeout, but I have ran in some cases where this interfered with the ongoing animation of the first call.

Here is how to use RxJs with Angular to implement debouncing.

In your template just bind a function to an event like you would normally:

(click)="doStuff()"

In your component use the following code:

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';

// ...
// ...
export class SomeComponent {
    private buttonClicked = new Subject();

    constructor() {
        const btnClickedObs = this.nextClicked
                                  .asObservable()
                                  .debounceTime(200);
        btnClickedObs.subscribe(() => 
                      this.functionToCall()
                      );
    }

    doStuff() {
        this.buttonClicked.next();
    }
}

Debouncing is similar to throttling. With throttling you limit the number of calls to 1 per time frame of unit so the first call happens automatically. If the user keeps mashing the button continuously, throttled calls will be made at a rate of 1 per throttleTime whereas with debouncing only one call will be made. Which one you want depends on your particular scenario.

You can mix both, but you must apply the debouncing before the throttling and the throttlingTime must be at least twice the debouncingTime. Otherwise one of them will have no effect.

Remove Hot Network Questions from StackOverflow

While I’m at work, I try to keep distractions at a minimum so I can concentrate on my work.

Like many .Net developers I tend to use StackOverflow a lot but one thing that gets the better of my curiosity is the Hot Network Questions tab on StackOverflow. I’ve clicked on non work related questions fare more time than I’d care to admit. To remedy this, on my workplace computer, I’ve made the HNQ tab go away.

hnq.PNG

Using the Grease Monkey Firefox add-on it’s possible to create JavaScript scripts, to modify the pages you surf.

I wrote a small script to make the entire thing go away on both StackOverFlow and SoftwareEngineering :

// ==UserScript==
// @name        HNQ Hider
// @description Hides the Stackexchange HNQ

// @include /^https?:\/\/(.*\.)?stackoverflow\.com/.*$/
// @include /^https?:\/\/(.*\.)?softwareengineering.stackexchange.com/.*$/

// @version     1
// @grant       none

// ==/UserScript==

document.getElementById('hot-network-questions').outerHTML='';

 

ASP.NET MVC: Dynamically adding an existing View as a Partial View to a parent

Let’s say you have a pre-existing Model/View/Controller. For my purposes I will call it FirstController, FirstModel and the view, Details.

In this post I will explain how to reuse this form and model in another View/Controller which will become it’s parent. I will also show how to dynamically add this child view to the parent via Ajax. This way we can conditionally add the view based on the user’s input.

Here is our pre-existing MVC:

namespace ChildModel.Controllers
{
    public class FirstController : Controller
    {
        public ActionResult Details()
        {
            var model = new FirstModel { Name = "Gilles",
                                         Number = 42 };
            return View(model);
        }

        [HttpPost]
        public ActionResult Details(FirstModel model)
        {
            if (!ModelState.IsValid)
            {
                return View();
            }

            return RedirectToAction("Index", "Home");
        }
    }
}

namespace ChildModel.Models
{
    public class FirstModel
    {
        public string Name { get; set; }
        public int Number { get; set; }
    }
}

With this simple view:

@model ChildModel.Models.FirstModel

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>First - Details</title>
</head>
<body>
<div>
<h1>First - Details</h1>
@using (Html.BeginForm("Details", "First"))
        {
            @Html.LabelFor(model => model.Name, "Name")
            @Html.TextBoxFor(model => model.Name)

            @Html.LabelFor(model => model.Number, "Number")
            @Html.TextBoxFor(model => model.Number)

            <input type="submit" value="Submit" />
        }</div>
</body>
</html>

first1

Here is the existing Model/View/Controller which will eventually become the parent:

namespace ChildModel.Controllers
{
    public class ParentController : Controller
    {
        public ActionResult Index()
        {
            var model = new ParentModel { IsChecked = true };
            return View(model);
        }

        [HttpPost]
        public ActionResult Submit(ParentModel model)
        {
            if (!ModelState.IsValid)
            {
                return View();
            }

            return RedirectToAction("Index", "Home");
        }
    }
}

namespace ChildModel.Models
{
    public class ParentModel
    {
        public bool IsChecked { get; set; }
    }
}
@model ChildModel.Models.ParentModel

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Parent</title>
</head>
<body>
<div>
<h1>Parent</h1>
@using (Html.BeginForm("Submit", "Parent"))
        {
            @Html.LabelFor(model => model.IsChecked, "Is Checked ?")
            @Html.CheckBoxFor(model => model.IsChecked)

            <input type="submit" value="Submit" />
        }</div>
</body>
</html>

parent1

Let’s start by including First into Parent. We will do the dynamic Ajax part afterwards.

Since we don’t want to include the form in our parent page, because our parent already has a form and submit button, we will take out the fields we want to reuse and put them in their own partial view.

_FirstFields.cshtml:

@model ChildModel.Models.FirstModel

@Html.LabelFor(model => model.Name, "Name")
@Html.TextBoxFor(model => model.Name)

@Html.LabelFor(model => model.Number, "Number")
@Html.TextBoxFor(model => model.Number)

And call this Partial View in First/Details.cshtml:

@using (Html.BeginForm("Details", "First"))
{
    Html.RenderPartial("_FirstFields");

    <input type="submit" value="Submit" />
}

In our ParentModel we will need to add a new property of type FirstModel:

public class ParentModel
{
    public bool IsChecked { get; set; }
    public FirstModel ChildModel { get; set; }
}

In our Parent/Index.cshtml view we can include the partial view. Notice the use of the ViewDataDictionary in RenderPartial. If we simply asked for the view, the model binder wouldn’t be able to get the data back on a Post. The HtmlFieldPrefix in our ViewDataDictionary is set to the name of our child model property.

This will have for effect of changing the Ids and names of the fields on the partial view allowing the model binder to correctly bind the fields.

@using (Html.BeginForm("Submit", "Parent"))
{
    @Html.LabelFor(model => model.IsChecked, "Is Checked ?")
    @Html.CheckBoxFor(model => model.IsChecked)

    Html.RenderPartial("../First/_FirstFields",
                       Model.ChildModel,
        new ViewDataDictionary(Html.ViewData)
        {
            TemplateInfo = new TemplateInfo
            {
                HtmlFieldPrefix = "ChildModel"
            }
        });

    <input type="submit" value="Submit" />
}

Now we have completed the first part by including a child model/view in our parent. Let’s add the Ajax part.

We will remove our RenderPartial and replace it with a div element which will serve to host the dynamic html code. We will also add an Ajax action link to call a new method on our ParentController.

@using (Html.BeginForm("Submit", "Parent"))
{
    @Html.LabelFor(model => model.IsChecked, "Is Checked ?")
    @Html.CheckBoxFor(model => model.IsChecked)

    @Ajax.ActionLink("Add Child Form", "GetChildElement",
                     "Parent",
                    new AjaxOptions
                    {
                        AllowCache = false,
                        InsertionMode =
                                InsertionMode.Replace,
                        HttpMethod = "Get",
                        UpdateTargetId = "childContainer",
                    })
<div id="childContainer"></div>
<input type="submit" value="Submit" />
}

In our ParentController we add the Ajax action method GetChildElement this time setting the prefix manually:

public PartialViewResult GetChildElement()
{
    ViewData.TemplateInfo.HtmlFieldPrefix = "ChildModel";
    return PartialView("../First/_FirstFields");
}

Don’t forget to add Ajax capabilities to your project like detailed here.

And everything should be working.

parent2

Error handling in ASP.NET MVC

This is a basic error handling strategy for an ASP.NET MVC app. It will handle all ASP.NET errors, allow somewhere to put logging code and redirect to a generic error page.

This will miss exceptions not handled by ASP.NET such as navigating to an invalid URL but will be sufficient to handle and log application errors in your controllers.

First start by creating an ErrorController (this one is basic but could be expanded):

using System.Web.Mvc;

namespace BasicErrorHandling.Controllers
{
    public class ErrorController : Controller
    {
        // GET: Error
        public ActionResult Index(string message)
        {
            TempData["errorMessage"] = message;
            return View();
        }
    }
}

Don’t forget to create a view for said controller:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Error</title>
</head>
<body>
    <div> 
        <h1>Generic Error Page</h1>
        <p>@TempData["errorMessage"]</p>
        
    </div>
</body>
</html>

For testing purposes, let’s introduce an exception in one of our actions:

public ActionResult About()
{
    ViewBag.Message = "Your application description page.";

    // throw an error to test exception handling
    throw new Exception("Fictious error");

    return View();
}

The method that will handle our errors and redirect to our error page is in our Global.asax.cs:

protected void Application_Error(object sender, EventArgs e)
{
    // get error and clear it 
    var exception = Server.GetLastError();
    Server.ClearError();

    // log error
    // ...

    // redirect to error page
    string message = Regex.Replace(exception.Message, @"\t|\n|\r", ""); // replace newlines which will not work with query string
    Response.Redirect("/Error/?message=" + message);
}

Here we should log or handle the error in the manner appropriate to our application. Finally we redirect to an error page supplying an optional message parameter. We could also redirect to this error controller from somewhere else than Global.asax.

To make everything work nicely we need to add a new route in our RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Error",
        url: "Error/{message}", 
        defaults: new { controller = "Error", 
                        action = "Index", 
                        message = UrlParameter.Optional } 
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", 
                        action = "Index", 
                        id = UrlParameter.Optional }
    );
}

 
You can find the whole thing on GitHub.