Contents tagged with javascript

  • Unobtrusive Data-Binding for Knockout.js

    Tags: knockout.js, data-binding, javascript

    If you're a web developer who has yet to see Knockout, go take a look. I'll wait... Knockout is an incredible library, I just love it. Though I prefer to keep my presentation layer clean and now it's sprinkled with little bits of code for the data-binding. And that is how this Plugin came to be. It solves that problem with an easy way to Unobtrusively Data-Bind your presentation layer.

    Obtrusive example (taken from knockoutjs.com)

    You'll notice in this example, information about how to perform the data-binding is married to the presentation layer.

    Choose a ticket class:
    <select data-bind="options: tickets, 
                       optionsCaption: 'Choose...',
                       optionsText: 'name',
                       value: chosenTicket"></select> 
    
    <p data-bind="template: 'ticketTemplate'"></p> 
            
    <script id="ticketTemplate" type="text/x-jquery-tmpl"> 
        {{if chosenTicket}}
            You have chosen <b>${ chosenTicket().name }</b>
            ($${ chosenTicket().price })
            <button data-bind="click: resetTicket">Clear</button>
        {{/if}}
    </script> 
        
    <script type="text/javascript"> 
        var viewModel = {
            tickets: [
                { name: "Economy", price: 199.95 },
                { name: "Business", price: 449.22 },
                { name: "First Class", price: 1199.99 }
            ],
            chosenTicket: ko.observable(),
            resetTicket: function() { this.chosenTicket(null) }
        };
        ko.applyBindings(viewModel);
    </script>	
    

    Unobtrusive Example

    But now the data-binding has been completely separated from the presentation layer.

    <!-- file: ticket.html -->
    
    Choose a ticket class:
    <select id="tickets"></select> 
    
    <p id="ticketOutput"></p> 
            
    <script id="ticketTemplate" type="text/x-jquery-tmpl"> 
        {{if chosenTicket}}
            You have chosen <b>${ chosenTicket().name }</b>
            ($${ chosenTicket().price })
            <button data-bind="click: resetTicket">Clear</button>
        {{/if}}
    </script> 
    

    and the javascript file...

    // file: ticket.js
    
    var viewModel = {
        tickets: [
            { name: "Economy", price: 199.95 },
            { name: "Business", price: 449.22 },
            { name: "First Class", price: 1199.99 }
        ],
        chosenTicket: ko.observable(),
        resetTicket: function() { this.chosenTicket(null) }
    };
     
    $('#tickets').dataBind({
        options: 'tickets',
        optionsCaption: "'Choose...'",
        optionsText: "'name'",
        value: 'chosenTicket'
    });
     
    $('#ticketOutput').dataBind({ template: "'ticketTemplate'" });
     
    ko.applyBindings(viewModel);
    

    The Advantages / Why

    Complete separation of code from your presentation layer -- There are many reasons why you might want this: Code can be kept in separate js files (think of caching), designers can now modify the html without worry of breaking it, etc. Elimination of magic strings-- Complex data-binding can be difficult to maintain with magic strings, especially when you start using functions or events. In the following example, I took the data-bound element from above and added a click event. Without the Unobtrusive approach, you'd have to encode that function into your magic string.

    $('#tickets').dataBind({
        options: "tickets",
        optionsCaption: "'Choose...'",
        optionsText: "'name'",
        value: "chosenTicket",
        event: {
            change: function(evt) {
                console.log(evt);
            }
        }
    });
    

    Download it

    Well that's pretty much it. The Plugin is small at only 544 bytes (360 bytes gzipped). Download it and play with it. Let me know if you find any bugs. You can download it here: github

  • MiniLinq for Script#

    Tags: script#, c#, javascript, linq

    Script# is my new favorite piece of technology. Write JavaScript with C#? Yes please! But since it's (currently) limited to ISO-2 C#, it can feel like you're stuck back in 2004. Using C# 2 means you're missing a lot of things you use daily... like LINQ. I do miss LINQ. So I read about Script# adding a few LINQ like extensions to the array class and decided to work them together into this MiniLinq class, which I am now sharing with you.

    Some examples

    This works with objects too, I'm just using int for simplicity.

    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    // [2, 4, 6, 8, 10]
    int[] evenNumbers = (int[])MiniLinq
        .From(numbers)
        .Where(delegate(object o) { return (int)o % 2 == 0; })
        .ToArray();
    
    // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    int[] descendingNumbers = (int[])MiniLinq
        .From(numbers)
        .OrderBy(delegate(object x, object y) { return x == y ? 0 : (int)x > (int)y ? -1 : 1; })
        .ToArray();
    
    // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
    int[] doubledNumbers = (int[])MiniLinq
        .From(numbers)
        .Select(delegate(object o) { return (int)o * 2; })
        .ToArray();
    
    

    I'm calling it MiniLinq because it's only a partial implementation of LINQ. To be more accurate, it's just the methods I need to use for my project. Until there's an official implementation in Script#, this is what I'll be using. If you want to download or extend MiniLinq, check it out on github. Share your changes with me!

  • Logging Errors with ELMAH in ASP.NET MVC 3 – Part 5 – (JavaScript)

    Tags: ASP.NET, MVC, Logging, Error Handler, Elmah, javascript

    Now we need a way to track those pesky client side error messages. Tracking the server side errors is the easy part, but what about those browser errors that go unnoticed and unfixed?

    First, create the Server Side Handler

    To handle and log JavaScript errors, we'll first need to setup some server side code. The client (browser) will pass these errors to the server, which will hand them off to ELMAH for tracking. So let's start by creating a new Exception type for our JavaScript exceptions. This will allow us to differentiate the server errors from the JavaScript errors more easily. It doesn't need much so this should be fine...

    public class JavaScriptException : Exception
    {
        public JavaScriptException(string message) : base(message)
        {
        }
    }
    

    We are also going to need a Controller to receive the exception and hand it off to ELMAH.

    public class ErrorController : Controller
    {
        public void LogJavaScriptError(string message)
        {
            ErrorSignal
                .FromCurrentContext()
                .Raise(new JavaScriptException(message));
        }
    }
    

    That's it for the server side changes. Next we need to setup the client (browser) to actually handle these errors and pass them onto the controller.

    JavaScript stack trace

    First we need to implement a JavaScript Stack Trace. This will help us get more debugging information and context surrounding the errors. So copy and paste this stacktrace.js file to your project...

    // FROM: http://helephant.com/2007/05/diy-javascript-stack-trace
     
    function logError(ex, stack) {
        if (ex == null) return;
        if (logErrorUrl == null) {
            alert('logErrorUrl must be defined.');
            return;
        }
     
        var url = ex.fileName != null ? ex.fileName : document.location;
        if (stack == null && ex.stack != null) stack = ex.stack;
     
        // format output
        var out = ex.message != null ? ex.name + ": " + ex.message : ex;
        out += ": at document path '" + url + "'.";
        if (stack != null) out += "\n  at " + stack.join("\n  at ");
     
        // send error message
        $.ajax({
            type: 'POST',
            url: logErrorUrl,
            data: { message: out }
        });
    }
     
    Function.prototype.trace = function()
    {
        var trace = [];
        var current = this;
        while(current)
        {
            trace.push(current.signature());
            current = current.caller;
        }
        return trace;
    }
     
    Function.prototype.signature = function()
    {
        var signature = {
            name: this.getName(),
            params: [],
            toString: function()
            {
                var params = this.params.length > 0 ?
                    "'" + this.params.join("', '") + "'" : "";
                return this.name + "(" + params + ")"
            }
        };
        if (this.arguments)
        {
            for(var x=0; x < this.arguments.length; x++)
                signature.params.push(this.arguments[x]);
        }
        return signature;
    }
     
    Function.prototype.getName = function()
    {
        if (this.name)
            return this.name;
        var definition = this.toString().split("\n")[0];
        var exp = /^function ([^\s(]+).+/;
        if (exp.test(definition))
            return definition.split("\n")[0].replace(exp, "$1") || "anonymous";
        return "anonymous";
    }
    
    window.onerror = function (msg, url, line) {
        if (arguments != null && arguments.callee != null && arguments.callee.trace)
            logError(msg, arguments.callee.trace());
    }
    

    Finally, add the Client Side Handlers

    Now we need add a reference the script in the template file and also initialize the JavaScript variable 'logErrorUrl' so errorhandler.js knows where to post the error details to.

    <script type="text/javascript">
      var logErrorUrl = '@Url.Action("LogJavaScriptError", "Error")';
    </script>
    <script src="@Url.Content("~/Scripts/errorhandler.js")" type="text/javascript"></script>
    

    That should be everything. All JavaScript errors should now be tracked via ELMAH.

    Testing the Error Handler

    If you want to test this, just to make sure it all works (which of course you do), then you can use this code to run some tests...

    <script type="text/javascript">
      function getPropertyData(value) {
        var x = value.property["data"];
      }
    
      function testCapturedError() {
        var x = getPropertyDataCaptured(null);
      }
    
      function getPropertyDataCaptured(value) {
        try {
          var x = value.property["data"];
        } catch (err) {
          if (arguments != null && arguments.callee != null && arguments.callee.trace)
              logError(err, arguments.callee.trace());
        }
      }
    </script>
    
    <ul>
    <li><a href="javascript:getPropertyData(null);">execute getPropertyData(null) method.</a></li>
    <li><a href="javascript:testCapturedError();">execute testCapturedError() method.</a></li>
    </ul>
    
    

    This page contains two exception tests. The first test will execute an unhandled exception. The second test will demonstrate a captured error (which you should always be doing) and stack trace and log it to ELMAH. Now when you check your ELMAH page, you should see the JavaScript exceptions show up like this...

    Elmah JavaScript Error

    Download the full project:

    ElmahAndMvc3.zip

    This Post is Part of a Multi-Part Series