Integrating Knockout into a Page

In these posts, I’m working through a process that enables me to implement an MVC/MVVM design pattern in my JavaScript code. In my first post, I discussed why I care (primarily to create a testable application) and set up my server side test functions. In the following post, I started creating the client-side ModelView that wrapped up both the Customer data that I wanted to display and the function that would retrieve the data from the server. I also created the test page that would demonstrate that the client-side Javascript ViewModel worked correctly. Now that I know that code works, it’s time to integrate it into a real page. Again, I’m going to take a Test Driven Development approach.

Creating Server-side Resources

My ViewModel’s getCustomer function will use jQuery’s getJson function to retrieve a Customer object. However, the server-side method I accessed from my test page returned a mock Customer object. For the real application, I need a method that returns a real Customer object. I’ll need to test that new Action that returns an actual Customer object, of course, so I add a Test project to my solution with a reference to my ASP.NET MVC project. This test runs the Action method that will (eventually) return the Customer object, catches the result and, through a Dynamic variable, checks to see if it returned the right result:

[TestClass]
public class CustomerMVVM
{
   [TestMethod]
   public void TestRetrieveCustomer()
   {
    HomeController  hc = new HomeController();
     System.Web.Mvc.JsonResult  jres = (System.Web.Mvc.JsonResult)
        hc.CustomerById("ALFKI");
     dynamic res = jres.Data;
     Assert.AreEqual("Berlin", res.City, "Unable to retrieve customer");
   }
}

Eventually, the Action method in my Controller that passes the test looks like this:

publicActionResult CustomerById(string id)
{
 using (northwndEntities ne = new northwndEntities())
 {
  Customer cust = (from c in ne.Customers
                       where c.CustomerID == id
                    select c).FirstOrDefault();
  ne.Detach(cust);
  return this.Json(cust, JsonRequestBehavior.AllowGet);
  };
}

That’s all I need in my Controller for now, so I’ll return to working in my client.

Binding Data from the ViewModel to the Page

In my View, I need to add a reference to the JavaScript file I created that holds my ViewModel code. I don’t want to put this reference in my Layout View because I only need this code in a few pages. Fortunately, when I set up my Layout View, I added a RenderSection statement in its <head> element. So, in my Customer View, I just need to insert that section with the script reference that I need:

@section Header 
{
 <script src="@Url.Content("~/Scripts/CustomerViewModel.js")"
    type="text/javascript"></script>
}

In the page, I need to do two things to start using my ViewModel. First, I need to activate Knockout so that it will handle moving data from my ViewModel to elements on the page (and moving data from the elements on the page back to my ViewModel). I do that by instantiating my ViewModel and passing it to Knockout’s applyBindings method as soon as the page is fully loaded. I also set my controller variable so that I when I start accessing my client-side methods, the URL will point to the right controller:

<scripttype="text/javascript">
    controller = "Home";
    $(function () {
        var cust = new Customer();
        ko.applyBindings(cust);
    });
</script>

I now need to tie the properties on my ViewModel to elements on my page—in this case, I’ll tie most of my data to <input> elements. To make that happen I add the data-bind attribute to the <input> element specifying which of Knockout’s bindings I want to use and the name of the property I’m binding to. Knockout’s text binding is the most useful since it works with both input and span elements (I could also have used Knockout’s value binding). The one exception to tying my data to input elements is my Customer Id property—I don’t want to allow users to update the Customer Id property (at least, not in the initial display) so I use a span element to display it. The following markup ties three elements are to the CustomerId, CompanyName, and City properties on the ViewModel I passed to Knockout’s applyBindings function:

<p>Id: <spandata-bind="text: CustId" /> </p>
<p>Name: <inputtype="text" data-bind="text: CompanyName" /> </p>
<p>City: <inputtype="text" data-bind="text: City" /> </p>

Binding Functions in the ViewModel to the Page

But I’m not limited to binding properties to elements. I can also bind functions on my ViewModel to events fired in the browser. For instance, I want to have a button on the page that allows users to delete the currently displayed customer. First, in my ViewModel I add a new function that calls a server-side method to handle deletions. In this method, I use my controller variable and my ViewModel’s CustId property (through the self variable I set up earlier) to build the URL I use to access the server-side method:

this.deleteCustomer = function ()
   {
    $.getJSON("/" + controller + "/DeleteCustomerById/" +  self.CustId,
         null,
         function (flag)
              {
              if (flag)
               {
                 self.CompanyName = "";
                 self.City = "";
               }
               else
	       {
                 alert("delete failed");
               }
              }
             );
    };

Which means that I need to create a mock server-side method to test this method against:

publicActionResult DeleteCustomerById(string id)
{
  return this.Json(false, JsonRequestBehavior.AllowGet);
}

I also need another test function on my test page to test my client-side code:

asyncTest("Delete Customer", function () {
            stop();
            expect(2);
            controller = "Test";
            cust = new Customer();
            cust.getCustomer("ALFKI");
            setTimeout(

			function () {
                            equals(cust.CustId(),"ALFKI", "Customer retrieved");
                            cust.deleteCustomer("ALFKI");
                            setTimeout(

			function () {
                                    equals(cust.CustId(),"", "Customer not deleted");
                                    start();
                                },
                                1000);
                         start();
                        },
                        1000);
        }
        );

Once my client-side code has passed all my tests, I move on to the server to create the production version of the DeleteCustomerById method. In true TDD fashion, I first create a test for the server-side method:

[TestMethod]
public void TestDeleteCustomer()
{
 HomeController hc = new HomeController();
 System.Web.Mvc.JsonResult jres = (System.Web.Mvc.JsonResult)hc.DeleteCustomerById("ALFKI");
				dynamic res = jres.Data;
 Assert.AreEqual(false, res, "Unable to delete customer");
}

Using that test to prove my code works, I create the Action method to use in production:

public ActionResult DeleteCustomerById(string id)
{
  try
  {
    using (northwndEntities ne = new northwndEntities())
    {
      Customer cust = (from c in ne.Customers
                    where c.CustomerID == id
                    select c).FirstOrDefault();
      ne.DeleteObject(cust);
      returnthis.Json(true, JsonRequestBehavior.AllowGet);
    };
  }
  catch
 {
      return this.Json(false, JsonRequestBehavior.AllowGet);
  }
}

The last step is to bind my deleteCustomer function I’ve just added to my ViewModel to the click event of a button. As with binding data, I use the data-bind attribute but this time I use Knockout’s click binding specifying the function on my ViewModel that the button’s click event is to call:

<button type="button"
    data-bind='click: deleteCustomer'>Delete Customer</button>

However, while I can manipulate the Customer object on my page, I haven’t actually written the code to retrieve that Customer object. That’s in my next post.

Type to search blog.learningtree.com

Do you mean "" ?

Sorry, no results were found for your query.

Please check your spelling and try your search again.

maltcasino maltcasino maltcasino bedava bahis güvenilir bahis siteleri canlı bahis siteleri
bedava bahis Yatırımsız Deneme Bonusu bonus veren siteler