Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Sunday, 20 January 2013

Hello World with KnockoutJs–Code Restructure

Recently I have been reading a number of articles and watching training videos from Pluralsight about Javascript and KnockoutJs. After reading the “Hello world, with Knockout JS and ASP.NET MVC 4!” by Amar Nityananda I wanted to see if I could apply the techniques of what I’d learnt to a relatively simple example. After reading the original article I couldn’t help but think “this Js could be better structured, I think I could do this” … so this is what I came up with.

To get an understanding of the problem please read the original article first.

I’ve only concentrated on the client side JavaScript as this was the challenge …

// setup the namespace
var my = my || {};

$(function () {

    my.Customer = function () {
        var self = this;
        self.id = ko.observable();
        self.displayName = ko.observable();
        self.age = ko.observable();
        self.comments = ko.observable();
    };

    my.vm = function () {
        var 
            // the storing array
            customers = ko.observableArray([]),
            // the current editing customer
            customer = ko.observable(new my.Customer()),
            // visible flag
            hasCustomers = ko.observable(false),
           
            // get the customers from the server
            getCustomers = function() {
                // clear out the client side and reset the visibility
                this.customers([]);
                hasCustomers(false);
               
                // get the customers from the server
                $.getJSON("/api/customer/", function(data) {
                    $.each(data, function(key, val) {
                        var item = new my.Customer()
                            .id(val.id)
                            .displayName(val.displayName)
                            .age(val.age)
                            .comments(val.comments);
                        customers.push(item);
                    });
                    hasCustomers(customers().length > 0);
                });
            },
            // add the customer from the entry boxes
            addCustomer = function() {
                $.ajax({
                    url: "/api/customer/",
                    type: 'post',
                    data: ko.toJSON(customer()),
                    contentType: 'application/json',
                    success: function (result) {
     // add it to the client side listing, this will add the id to the record;
     // not great for multi user systems as one client may miss an updated record
     // from another user
                        customers.push(result);
                        customer(new my.Customer());
                    }
                });
            };

        return {
            customers: customers,
            customer: customer,
            getCustomers: getCustomers,
            addCustomer: addCustomer,
            hasCustomers: hasCustomers
        };
    } ();

    ko.applyBindings(my.vm);
});

The main points I was trying to cover are:

  • Code organisation through namespaces – I don’t want any potential naming conflicts. This isn’t a big issue in a small application but once they start growing it’s good to try and avoid putting definitions into the global namespace.
  • Expose the details of the view model through the Revealing Module pattern – this keeps details clean as well as improved data binding clarity.

I haven’t gone into too much detail as the rest of the project infrastructure is very similar to the original article and can be found there. I wanted to post this as an example of how to make the Js structured.

Happy to discuss further; contactable in the comments or via twitter.

Updated Edit

After a comment from ranga I have read the link posted and update the code (see below). I have moved the view model and the Customer declaration outside of the on document ready event. I have also change hasCustomers to be a computed value. The interesting read about the performance hit about the events firing on array manipulation was really interesting – worth a read.

// setup the namespace
var my = my || {};

my.Customer = function () {
    var self = this;
    self.id = ko.observable();
    self.displayName = ko.observable();
    self.age = ko.observable();
    self.comments = ko.observable();
};

my.vm = function () {
    var
    // the storing array
    customers = ko.observableArray([]),
    // the current editing customer
    customer = ko.observable(new my.Customer()),
    // visible flag
    hasCustomers = ko.computed(function () {
        return customers().length > 0;
    }),

    // get the customers from the server
    getCustomers = function () {
        // clear out the client side
        this.customers([]);

        // get the customers from the server
        $.getJSON("/api/customer/", function (data) {
            var items = new Array();
            $.each(data, function (key, val) {
                var item = new my.Customer()
                    .id(val.id)
                    .displayName(val.displayName)
                    .age(val.age)
                    .comments(val.comments);
                items.push(item);
            });
            customers(items);
        });
    },

    // add the customer from the entry boxes
    addCustomer = function () {
        $.ajax({
            url: "/api/customer/",
            type: 'post',
            data: ko.toJSON(customer()),
            contentType: 'application/json',
            success: function (result) {
                // add it to the client side listing, this will add the id to the record;
                // not great for multi user systems as one client may miss an updated record
                // from another user
                customers.push(result);
                customer(new my.Customer());
            }
        });
    };

    return {
        customers: customers,
        customer: customer,
        getCustomers: getCustomers,
        addCustomer: addCustomer,
        hasCustomers: hasCustomers
    };
} ();

$(function () {
    ko.applyBindings(my.vm);
});

Keep the feedback coming, I’m always up for learning and improving.

Sunday, 23 September 2012

Opening Pluralsight Single Page Application course code in VS2010

John Papa published a very good course on Pluralsight the other day about how to create a Single Page Application (SPA) and due to the popularity of the course Pluralsight opened it up for a short time only for free (thanks!). This allowed me to see exactly the type of quality I would receive when my training budget at work comes through soon (fingers crossed for October).

After watching some of the videos it was really interesting but I wasn’t quite getting how it all fitted together in the solution so was very pleased to find that the free sign up included being able to download the course material including the final solution. After downloading the zip file, expanding the contents and locating the .sln file I double clicked it and waited for Visual Studio 2010 to fire it up.

image

On Visual Studio 2010 opening the solution I was presented with a very sad looking solution explorer.

image

This is due to the fact that the course was written in Visual Studio 2012 and hence .net 4.5 was used for the projects. This got me thinking that it shouldn’t cause a problem as all the technologies which were being used are also available for .net 4 so I got about trying to get it to work.

Step 1; getting the projects to load

This in itself wasn’t that tricky. Firing up the .csproj files in notepad and locating the TargetFrameworkVersion I changed it to “v4.0”, saved and repeated it for all of the projects in the solution.

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

To

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

Once all the projects had been updated I reopened the solution in Visual Studio 2010 and got a prompt about configuring IISExpress. CodeCamper is configured to use IISExpress however if you don’t have it installed you will need to install it or configure the solution to work on a local IIS instance.

image

On clicking “Yes” Visual Studio attempted to load up the projects and Resharper did it’s usual solution scan and I was presented with a much happier looking solution explorer.

image

Step2; Lets try and build

On building the solution I got the following warnings however it did build.

warning CS1684: Reference to type 'System.Data.Spatial.DbGeometry' claims it is defined in 'c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Data.Entity.dll', but it could not be found
warning CS1684: Reference to type 'System.Data.Spatial.DbGeography' claims it is defined in 'c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Data.Entity.dll', but it could not be found

After a “successful” build I fired up the IISExpress site and went to start browsing the site to start seeing how it fitted together. However this was not the case and I got the following error.

image

Step3; changing 4.5 to 4

The changing of required framework version had not finished at the csproj level so I went through the web.config of the asp.net mvc project and changed the targetFramework attribute to “4.0”.

I also removed targetframework=4.5 from the httpruntime element below. Removed the machine key below that and hit refresh.

image

By now I was pretty sure that dependencies and reference errors are likely to be due to the framework version. With this I did a little Google and found that the version number had been bumped up to 4 for System.Net.Http in .net 4.5 however for .net 4.0 this needs to continue to reference version 2. With this in mine I updated the web.config from:

<dependentAssembly>
  <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>

to

<dependentAssembly>
  <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>

and hit refresh.

image

At this point I decided I would ignore the AntiXss encoder reference for now and come back to it later. I’m not going to publish the site anywhere so not worried about cross site scripting. However if you are going to put a site up into the wild then you will need to stop this kind of attack. Discussion for this is out of scope for this post.

I hit refresh and was presented with the Code Camper blue screen as below.

image

However no data.

step 4; getting data

At this point I thought I’m pretty close and pretty sure this will have something to do with the connection string. So after checking the connection strings configured in the web.config and noticing which one was currently active I checked to make sure the CodeCamper.sdf was present and ran.

image

Nothing; so changed the CodeCamperDatabaseInitializer below to always drop and recreate the DB to make sure the DB was fresh with the seeded data (remember to remove this and put it back to one of the other options otherwise it will delete everything all the time).

public class CodeCamperDatabaseInitializer :
    //CreateDatabaseIfNotExists<CodeCamperDbContext> // when model is stable
    //DropCreateDatabaseIfModelChanges<CodeCamperDbContext> // when iterating
    DropCreateDatabaseAlways<CodeCamperDbContext>

Nothing. At this point I decided to remove the sdf file and update the connection string to use to be the SQLExpress one. I knew I had that installed and running so would be perfectly fine for study purposes.

Time to try running it again to see which error would occur next. The next error was inside the NInject Dependency Scope implementation, in the NinjectDependencyScope.GetService(Type serviceType) method …

image

… with the wonderfully descriptive error message of

“Method not found: ‘System.Delegate System.Reflection.MethodInfo.CreateDelegate(System.Type)”.

This was due to a version miss match of Ninject; uninstalling the Ninject nuget package and reinstalling it to get the correct version down seemed to resolve this.

Another rebuild and run. This time the EntityFramework was not happy with the current build. After the last issue with Ninject I thought I’d try the same routine so I uninstalled the EntityFramework nuget package and reinstalled it. It seemed to be originally wanting .net 4.5 but it’s happy with 4. This needed to be done in both the CodeCamper.Data and CodeCamper.Web projects.

At this point I hit f5 and crossed my fingers. I was greeted with a screen full of green friendly messages about getting data from the asp.net web api and a smiling photo of the mighty ScottGu; finally I’d got it working!

image

Conclusion

There is one out standing issue which was not resolved and that is around the AntiXss functionality but the rest seems to work as expected.

Thanks again to John Papa and Pluralsight for creating and publishing this course and I can’t wait to get my full subscription to start enjoying the other courses.

Wednesday, 20 June 2012

Testable client side view model with knockout without any server side dependencies

After reading the blog post series looking at creating a testable application with a JavaScript by Peter Vogel which looked at setting up the server side and the client side view model using Knockout I couldn’t help but think JavaScript needs a way of doing a kind of dependency injection so it can be unit testable without the reliance on the server side.

I understand that it will have a reliance on the server side to output the tests, however this is down to the implementation of the project not a requirement of unit testing.

View model creation

The view model is setup in a very similar way to how Peter had it in his example with a couple of tweaks to allow for the “DI” to function.

var Customer = function () {
    this.CustId = ko.observable();
    this.CompanyName = ko.observable();
    this.City = ko.observable();

    var self = this;

    this.getCustomer = function (custId) {

        MyApp.Customer.getCustomer(custId, this.updateData);

    };

    this.updateData = function (data) {
        self.CustId(data.CustomerID);
        self.CompanyName(data.CompanyName);
        self.City(data.City);
    };
}

We start by setting up the properties of the view model with the Knockout observables, set a reference to it’s self to be able to reference them later (good practice with Knockout from what I can gather), a callback binding function and the first “action” function on the model; getCustomer.

The getCustomer method calls into the data service passing in the parameters it requires for the action to execute and a callback function to call on the result. This could easily be expanded so that error handling can be caught and handled by another callback function but for this we’ll keep it simple.

With the view model set, the data service contract defined and being called in the function the view model is clean and not cluttered. At this point we can take a look at the data service “interface”.

Client side data service

var MyApp = MyApp || {};
MyApp.Customer = {};
MyApp.Customer.getCustomer = function (custId, callback) { };

The data service is built up with JavaScript object notation. The first two lines is to make sure that firstly there is a “MyApp” defined and it has a “Customer” property. Then the getCustomer function is defined with the set parameters required for execution and the delegate for the callback. Doing it this way you also add it into your own applications namespace and avoid cluttering up the global name space.

Lets look at the implementations for unit testing and for the actual application implementation.

The Unit Test

The test we are trying to pass is relatively simple and still based on Peter’s from the original series.

test("Get Customer",
    function () {
        stop();
        cust = new Customer();
        cust.getCustomer("ALFKI");
        setTimeout(function () {
            equals(cust.CustId(), "ALFKI", "ID not correct");
            equals(cust.CompanyName(), "My Company", "Company not correct");
            equals(cust.City(), "Reading", "City not correct");
            start();
        },
    3000);
    })

All it is trying to do is call getCustomer and make sure the data returned is bound to the correct properties.

Data service in testing

For testing purposes the view model test requires a populated JSON object to be returned from the data service. To enable the test to pass and remove the reliance on the server side test controller I create a mock client side data service before the qunit tests are added to the page.

var MyApp = MyApp || {};
MyApp.Customer = {};
MyApp.Customer.getCustomer = function (custId, callback) {
callback({ CustomerID: custId, CompanyName: "My Company", City: "Reading" });
};

All this is doing is following the contract/interface as defined for the data service, but instead of calling into a server side action it is returning a “mock” object. At this point we have removed the need for any server side calls.

After this has been defined we can now write our unit test (as above) and test that JSON object values which are being returned are bound to the correct properties.

Application implementation

So now we’ve created a mock data service and got our unit tests passing for the view model it’s time to create the actual implementation to allow for server communication and getting actual data.

var MyApp = MyApp || {};
MyApp.Customer = {};
MyApp.Customer.getCustomer = function (custId, callback) {
    var url = "@Url.Action("CustomerById", "Home")/" + custId;
    $.getJSON(url, null, callback);
};

As you can see we follow the same pattern as before but the difference is the getCustomer body implementation. We can now call into the server action and using jQuery’s $.getJSON method retrieve some json data from a server. The callback function is passed into the $.getJSON method which will delegate what needs to be done when the data is returned.

Conclusion

So to conclude client side unit tests do not need to have reliance on server side actions to execute even if they require data to be returned from an action. Some people may argue that the unit test isn’t testing the responses back from the server and handling them correctly. To do this you could go down the test controller route and get the server side to through actual errors or you could get the mock call to throw the errors.

If you have any comments about the above please post them below or contact me on Twitter - @WestDiscGolf.