DotNetCurry Logo

Business Application using HTML5, ASP.NET MVC, Web API and Knockout.js - Part 2

Posted by: Mahesh Sabnis , on 10/29/2015, in Category ASP.NET MVC
Views: 18117
Abstract: Combining rich JavaScript libraries like jQuery and Knockout makes ASP.NET MVC & HTML5 applications shine with their rich features like supporting AJAX and two-way binding.

In Part 1 of the article series of creating Business apps using HTML5, ASP.NET MVC and Knockout, we saw the server-side bits like Database design, Model design, and MVC and Web API controllers. We created Web API controllers to handle creation of Owner, property registration, Customer creation along with its property search features. We also created MVC controllers to create views for Owner, Customer, etc.

 

In this part of article we will implement the client-side logic using jQuery and Knockout library. To implement the steps shown in this article, Part 1 of the article must be implemented http://www.dotnetcurry.com/aspnet-mvc/1199/business-app-html5-aspnet-mvc-webapi-knockoutjs .

Step 1: Assuming you have downloaded the source code from Part 1, in the Scripts folder, add a new folder of name ‘MyScripts’. In this folder, add a new JavaScript file of name ‘Owner.js’. In this file, add the following logic:

(function () {
    var OwnerViewModel = function () {
        var self = this;
        
      //This function is declared in the Index.cshtml of the Owner View
   var data =  GlobalDeclaration.UserName(); //Get the Current LogIn User

        getOwnerIdUsingEmail(); //Load the Ownerdetails if found

        self.OwnerId = ko.observable(0);  //The OwnerId
        self.OwnerName = ko.observable(""); //The Owner Name
        self.Address = ko.observable(""); //The Address
        self.City = ko.observable(""); //The City
        self.Email = ko.observable(""); //The Email

         
        self.Email(data); //Set the login Email to the Email Observable

        self.Contact1 = ko.observable(""); //The Field for Primary Contact
        self.Contact2 = ko.observable("");//The Field for Secondary Contact

        self.ErrorMessage = ko.observable("");


        //The Owner Object will be used for Adding Owner
        var Owner = {
            OwnerId: self.OwnerId,
            OwnerName: self.OwnerName,
            Address: self.Address,
            City: self.City,
            Email: self.Email,
            Contact1: self.Contact1,
            Contact2:self.Contact2
        };

        //The Function is to Save Owner
        self.save = function () {
            $.ajax({
                url: "/api/OwnerInfoAPI",
                type: "POST",
                data: Owner,
                datatype: "json",
                contenttype:"application/json;utf-8"
            }).done(function (resp) {
                self.OwnerId(resp.OwnerId);
            }).error(function (err) {
                self.ErrorMessage("Error!  " + err.status);
            });
        }


        self.cancel = function () {
            self.OwnerId(0);
            self.OwnerName("");
            self.Address("");
            self.City("");
            self.Email("");
            self.Contact1("");
            self.Contact2("");
        }

        //The Function will decide whether to Register Properties
        self.canRegisterProperty = function () {
            var canRegister = false;
            if (self.OwnerId() > 0)
            {
                canRegister = true;
            }
            return canRegister;
        }

        //Function to Get the Owner Details based upon Email
        function getOwnerIdUsingEmail() {
            alert("Wel-Come " + data);
            $.ajax({
                url: "/Owner/Get",
                type: "GET"
            }).done(function (resp) {
                if (resp.OwnerId !== 0)
                {
                    self.OwnerId(resp.OwnerId);
                    self.OwnerName(resp.OwnerName);
                    self.Address(resp.Address);
                    self.City(resp.City);
                    self.Email(resp.Email);
                    self.Contact1(resp.Contact1);
                    self.Contact2(resp.Contact1);
                }
            }).error(function (err) {
                alert("Error Occured");
                self.ErrorMessage(err.status);
                alert(JSON.stringify(Owner));
            });
        }
    };
    ko.applyBindings(new OwnerViewModel());
})();

The above JavaScript contains the following functionality:

  • OwnerViewModel is the Knockout view-model that declares observables for the Owner Information.
  • ‘GlobalDeclaration.UserName’ is declared in the Index.cshtml view of the OwnerController’s Index action method. This passes the current Login Email to the View-Model declared using Knockout.
  • The function ‘save’ makes an Ajax call to the OwnerInfoAPI to Post the Owner information.
  • The ‘cancel’ function clears all textboxes.
  • The ‘canRegisterProperty’ function checks if the ‘OwnerId >0’, i.e. if the Owner information is successfully stored in DB then the condition will be true. This is then exposed to the View.
  • The ‘getOwnerIdUsingEmail’ function gets the owner information based upon the Email. This information will be displayed on the View.

In the Content folder, add a new folder of name ‘MyStyles’. In this folder add a new css file of name Owner.css and add the following styles in it:

#tblowner, .c0 {
  border:double;
  width:500px;
}
#dverror {
  height:200px;
  width:500px;
}

Open OwnerController class and Scaffold an empty View from the Index action method of name ‘Index.cshtml’ as below:

i9-index-owner

Add the following HTML markup with the Script references and the Knockout DataBinding expressions as below:

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
 
<link href="~/Content/MyStyles/Owner.css" rel="stylesheet" />

<script type="text/javascript">
    var GlobalDeclaration = {
       UserName:function(){
           return '@ViewBag.CurrentUserEmail';
        }
    };
   // var global = '@ViewBag.CurrentUserEmail';
</script>

<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/knockout-3.3.0.js"></script>

<form data-bind="submit:$root.save">

    <table id="tblowner" class="table table-bordered table-striped">
        <tr>
            <td class="c0">OwnerId</td>
            <td class="c0">
                <input type="text" id="ownerid" readonly="readonly"
                       data-bind="value:$root.OwnerId" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Name</td>
            <td class="c0">
                <input type="text" id="ownername" required
                       data-bind="value:$root.OwnerName" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Address</td>
            <td class="c0">
                <textarea id="owneraddress" required
                          data-bind="value:$root.Address" class="form-control"></textarea>
            </td>
        </tr>
        <tr>
            <td class="c0">City</td>
            <td class="c0">
                <input type="text" id="ownercity" required
                       data-bind="value:$root.City" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Email</td>
            <td class="c0">
                <input type="text" id="owneremail" readonly="readonly"
                       data-bind="value:$root.Email" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Primary Contact</td>
            <td class="c0">
                <input type="text" id="ownercontact1" required
                       data-bind="value:$root.Contact1" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Secondary Contact</td>
            <td class="c0">
                <input type="text" id="ownercontact2" required
                       data-bind="value:$root.Contact2" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">
                <button id="btnsave" type="submit" class="btn btn-success">Save</button>
            </td>
            <td class="c0">
                <button id="btncancel"
                        data-bind="click:$root.cancel" class="btn btn-default">
                    Clear
                </button>
            </td>
        </tr>
    </table>
</form>
<div data-bind="visible:$data.OwnerId()>0">
    @Html.ActionLink("Continue to Register Property", "Index",
                            "OwnerPropertyDescription")
</div>
<div id="dverror">
    <span data-bind="text:$root.ErrorMessage"></span>
</div>
<script src="~/Scripts/MyScripts/Owners.js"></script>


The above View uses the ViewModel declared in the Owners.js.

In the above View, check out the GlobalDeclaration() function. This declares ‘UserName’ function which accepts the Current Login User’s Email from the Controller. The View has a <form> tag which is bound with ‘submit’ binding to the ‘save’ function declared in the Knockout view-model. Each Input type on the View is bound with the observable declared in the knockout. The View contains <div> tag at the end of the View, which is bound with the ‘visible’ binding to the condition ‘OwnerId()>0’. If the condition is true then the Action Link will be visible which allows the Owner to register properties.

In the ‘MyScripts’ folder, add a new JavaScript file of name ‘OwnerPropertyDescription.js’ and add the following code:

(function () {

    var OwnerPropertyDescriptionViewModel = function () {
        var self = this;

        var data = GlobalDeclaration.UserName(); //Get the Current LogIn User

        self.OwnerPropertyId = ko.observable(0); //For OwnerPropertyId
        self.OwnerId = ko.observable(0); //For OwnerId
        self.OwnerName = ko.observable(""); //For Owner Name
        self.Address = ko.observable(""); //For Address
        self.BedRoomNo = ko.observable(0); //For No. of Bedrooms
        self.TotalRooms = ko.observable(0); //For Total Rooms
        self.PropertyBuildupArea = ko.observable(0);//For the area
        self.PropertyDescription = ko.observable(); //For Property Description
        self.PropertySaleRentStatus = ko.observable(); //For the Status as Rent or Sale
        self.SaleOrRentCost = ko.observable(); //The Cost for Sale to Rent
        self.PropertyAge = ko.observable(); //The Age of the property
        self.Status = ko.observable(); //The Status of the Property as 'Available'
        self.RegistrationDate = ko.observable(); //For the Registration Date

        self.PropertyType = ko.observable(); //The Property Type (Flat, Bunglow, RowHouse,etc)

        self.ErrorMesage = ko.observable("");

        self.PropertyTypes = ko.observableArray([
            "Flat", "Bunglow","Row House"
        ]); //Array for Property Types

        self.SelectedProperty = ko.observable();

        //Flag for Selection of Property and Status
        var propertySelected =0, saleORrentStatusSelected =0;

        //This will provide the selected property type id
        self.SelectedProperty.subscribe(function (val) {
            if(val!=='undefined'){
            propertySelected = 1;
            self.PropertyType(val);
            }
        });


        //The Array used to Store properties by owner
        self.PropertyByOwner = ko.observableArray([]); //Array for Property Owners

        //Array for Sale or Rent
        self.PropertySalesOrRentStatusConstant = ko.observableArray([
            "Sale","Rent"
        ]);

        //An observable
        self.SelectedStatus = ko.observable();

        //This will decide whether the property is for Rent or sales
        self.SelectedStatus.subscribe(function (text) {
            if (text !== 'undefined') {
                saleORrentStatusSelected=1
                self.PropertySaleRentStatus(text);
            }
        });

        
        //The Owner Description Object
        var OwnerPropertyDescription = {
            OwnerPropertyId: self.OwnerPropertyId,
            PropertyType: self.PropertyType,
            OwnerId: self.OwnerId,
            Address: self.Address,
            BedRoomNo: self.BedRoomNo,
            TotalRooms: self.TotalRooms,
            PropertyBuildupArea: self.PropertyBuildupArea,
            PropertyDescription: self.PropertyDescription,
            PropertySaleRentStatus: self.PropertySaleRentStatus,
            SaleOrRentCost: self.SaleOrRentCost,
            PropertyAge: self.PropertyAge,
            Status:self.Status
        };
       

        //Function call to load all propeties by Owner
        loadPropertiesbyowner();
       


       //Function to load all properties by owner 
        function loadPropertiesbyowner() {
                 $.ajax({
                     url: "/api/OwnerPropertyDescriptionAPI",
                    type: "GET"
                }).done(function (resp) {
                    self.PropertyByOwner(resp);
                }).error(function (err) {
                    self.ErrorMesage("Error " + err.status);
                });
        }

       //Save the Description. Saves the Property description if the 
        //Property tyoe and SaleRent Status is selected
        self.save = function () {

            if (propertySelected === 1 && saleORrentStatusSelected === 1)
                {
                    self.Status("Available");
                    $.ajax({
                        url: "/api/OwnerPropertyDescriptionAPI",
                        type: "POST",
                        data: OwnerPropertyDescription,
                        datatype: "json",
                        contenttype:"application/json;utf-8"
                    }).done(function (resp) {
                        self.OwnerPropertyId(resp.OwnerPropertyId);
                        loadPropertiesbyowner();

                    }).error(function (err) {
                        self.ErrorMesage("Error " + err.status);
                    });
               }
            propertySelected = 0;
            saleORrentStatusSelected = 0;
        }

        //Clear Inpit Elements
        self.cancel = function () {
            self.OwnerPropertyId(0);
            self.PropertyType("");
            self.Address("");
            self.BedRoomNo(0);
            self.TotalRooms(0);
            self.PropertyBuildupArea(0);
            self.PropertyDescription("");
            self.PropertySaleRentStatus("Choose Sale or Rent");
            self.SaleOrRentCost(0);
            self.PropertyAge(0);
            self.Status("")
        }

        getOwnerIdUsingEmail();
        //Function to Get the OwnerId using Email
        function getOwnerIdUsingEmail() {
            $.ajax({
                url: "/Owner/Get",
                type:"GET"
            }).done(function (resp) {
                self.OwnerId(resp.OwnerId);
                self.OwnerName(resp.OwnerName);
            }).error(function (err) {
                self.ErrorMesage(err.status);
            });
        }

    };
    ko.applyBindings(new OwnerPropertyDescriptionViewModel());

})();

The above file contains the following functionality:

  • Declaration of Knockout ViewModel for OwnerPropertyDescription.
  • The ‘loadPropertiesbyOwner’ function loads all properties registered by owner.
  • The ‘save’ function saves the data by making call to Web API when the Owner selects the Property type and the status of the registration i.e. Sale or Rent.
  • The ‘getOwnerIdUsingEmail’ function gets the Owner information based upon the Email by making call to Web API.

From the ‘OwnerPropertyDescription’ controllers Index action method, scaffold an empty View of name Index.cshtml. In the View, add the below markup with the Knockout DataBinding expressions:

@{
    ViewBag.Title = "Index";
}
<style type="text/css">
    #tblproperties, .c0 {
        border: double;
    }

    #dvcontainer {
         width: 1200px;
        height: 200px;
        overflow-x: visible;
        overflow-y: scroll;
    }

    .c3 {
     background-color:yellow;
        border:double;
     width:100px;

    }
</style>

<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/knockout-3.3.0.js"></script>

<script type="text/javascript">
    var GlobalDeclaration = {
        UserName: function () {
            return '@ViewBag.CurrentUserEmail';
        }
    };
 
</script>




<h2>Index</h2>

<div id="dvcontainer">
    <table id="tblproperties" class="table table-bordered table-striped">
        <thead>
            <tr>
                <td class="c3">
                    Property No
                </td>
                <td class="c3">
                    The Property Type
                </td>
                <td class="c3">
                    Address
                </td>
                <td class="c3">
                    No of Bedrooms
                </td>
                <td class="c3">
                    Total Rooms
                </td>
                <td class="c3">
                    Buildup Area
                </td>
                <td class="c3">
                    Description
                </td>
                <td class="c3">
                    Property Status
                </td>
                <td class="c3">
                    Cost
                </td>
                <td class="c3">
                    Property Age
                </td>
                <td class="c3">
                    Status
                </td>
                <td class="c3">
                    Registration Date
                </td>
            </tr>
        </thead>
        <tbody data-bind="foreach:PropertyByOwner">
            <tr>
                <td class="c0">
                    <span data-bind="text:OwnerPropertyId"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:PropertyType"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:Address"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:BedRoomNo"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:TotalRooms"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:PropertyBuildupArea"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:PropertyDescription"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:PropertySaleRentStatus"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:SaleOrRentCost"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:PropertyAge"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:Status"></span>
                </td>
                <td class="c0">
                    <span data-bind="text:RegistrationDate"></span>
                </td>
                
            </tr>
        </tbody>
    </table>
</div>

<form data-bind="submit:$root.save">

    <div id="dvregisterproperty">
        <table class="table table-bordered table-striped">
            <tr>
                <td>
                    Owner Poperty Id:
                </td>
                <td>
                    <input type="text" id="ownerpropertyid" data-bind="value:$root.OwnerPropertyId" class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>
                    Property Type:
                </td>
                <td>
                    <select id="lstpropertytypes" data-bind="options:PropertyTypes,optionsCaption:'Choose Property Type',value:SelectedProperty"></select>
                </td>
            </tr>
            <tr>
                <td>
                    Owner Name:
                </td>
                <td>
                    <input type="hidden" id="ownerid" data-bind="value:$root.OwnerId" />
                    <input type="text" id="ownername" data-bind="value:$root.OwnerName" class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>Address</td>
                <td>
                    <textarea id="address" data-bind="value:$root.Address" required class="form-control"></textarea>
                </td>
            </tr>
            <tr>
                <td>
                    No. Of BedRooms:
                </td>
                <td>
                    <input type="text" id="noofbedrooms" data-bind="value:$root.BedRoomNo"
                            required class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>
                    Total Rooms:
                </td>
                <td>
                    <input type="text" id="noofrooms" data-bind="value:$root.TotalRooms"
                           required class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>
                    Build Up Area:
                </td>
                <td>
                    <input type="text" id="builduparea" data-bind="value:PropertyBuildupArea"
                           required class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>
                    Property Description:
                </td>
                <td>
                    <textarea id="desc" data-bind="value:PropertyDescription"
                              Required class="form-control"></textarea>
                </td>
            </tr>
            <tr>
                <td>
                    Published For (Sale/Rent):
                </td>
                <td>
                    <select id="lstSaleRent"
                            data-bind="options:PropertySalesOrRentStatusConstant,optionsCaption:'Choose Sale or Rent',value:SelectedStatus"></select>
                </td>
            </tr>
            <tr>
                <td>
                    The Expected Cost
                </td>
                <td>
                    <input type="text" id="expectedcost" data-bind="value:$root.SaleOrRentCost"
                           required class="form-control"/>
                </td>
            </tr>
            <tr>
                <td> Property Age </td>
                <td>
                    <input type="text" id="propertyage" data-bind="value:$root.PropertyAge"
                           required class="form-control"/>
                </td>
            </tr>
            <tr>
                <td>
                    <input type="submit" value="Save" id="btnsave" class="btn btn-success"/>
                </td>
                <td>
                    <input type="button" value="Cancel" id="btncancel" data-bind="click:$root.cancel" class="btn btn-default"/>
                </td>
            </tr>
        </table>
        <div>
            <span data-bind="text:$root.ErrorMesage"></span>
        </div>
    </div>
</form>
<script src="~/Scripts/MyScripts/OwnerPropertyDescription.js"></script>

Note: The above view is bound with the ViewModel declared in OwnerPropertyDescription.js

 

In the above markup the DataBinding expression binds the observables, observableArray and functions, with the View.

In the MyScripts folder, add a new JavaScript file of the name ‘Customers.js’ and add the following JavaScript code in it:

(function () {
    var CustomerViewModel = function () {
        var self = this;

        var data = GlobalDeclarationCust.UserName();


        getCustomerByEmail();

        self.CustomerId = ko.observable(0); //For Customer Id
        self.CustomerName = ko.observable(""); //For Customer Name
        self.Address = ko.observable(""); //For Address
        self.City = ko.observable(""); //For City
        self.Email = ko.observable(""); //For Email
        self.Contact1 = ko.observable(""); //The Field for Primary Contact
        self.Contact2 = ko.observable("");//The Field for Secondary Contact

        self.Email(data);

        self.ErrorMessage = ko.observable("");

        var Customer = {
            CustomerId: self.CustomerId,
            CustomerName: self.CustomerName,
            Address: self.Address,
            City: self.City,
            Email: self.Email,
            Contact1: self.Contact1,
            Contact2: self.Contact2
        };

        //Function to get Customer info based upon the Login Email 
        function getCustomerByEmail() {
            $.ajax({
                url: "/Customer/Get",
                type:"GET"
            }).done(function (resp) {
                if (resp.CustomerId !== 0) {
                    self.CustomerId(resp.CustomerId);
                    self.CustomerName(resp.CustomerName);
                    self.Address(resp.Address);
                    self.City(resp.City);
                    self.Contact1(resp.Contact1);
                    self.Contact2(resp.Contact2);
                    self.Email(resp.Email);
                }
            }).error(function (err) {
                self.ErrorMessage("Error!!!" + err.status);
            });
        }
        //Save the Customer
        self.save = function () {

           // alert("Data" + ko.toJSON(Customer));
            $.ajax({
                url: "/api/CustomerInfoAPI",
                type: "POST",
                data: Customer,
                datatype: "json",
                contenttype:"application/json;utf-8"
            }).done(function (resp) {
                self.CustomerId(resp.CustomerId);
            }).error(function (err) {
                self.ErrorMessage("Error " + err.status);
            });
        };

        self.clear = function () {
            self.CustomerId(0);
            self.CustomerName("");
            self.Address("");
            self.City("");
            self.Email("");
            self.Contact1("");
            self.Contact2("");
        }
    };
    ko.applyBindings(new CustomerViewModel());
})();

The above view-model defines observables, observableArray functions for performing Customer related operations. The customer information is saved using save() function. This function makes an Ajax call to Web API to save customer information.

In the MyStyles folder, add a new css file and name it as ‘Customers.css’ with the following styles:

#tblcustomer, .c0 {
  border:double;
}

Using the Index action method of the CustomerController, scaffold an Empty View of the name Index.cshtml with following HTML markup and Data-Binding expressions as shown here:

@{
    ViewBag.Title = "Index";
}
 
<h2>Index</h2>
<link href="~/Content/MyStyles/Customers.css" rel="stylesheet" />

<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/knockout-3.3.0.js"></script>

<script type="text/javascript">
    var GlobalDeclarationCust = {
        UserName: function () {
            return '@ViewBag.CurrentUserEmail';
        }
    };
    // var global = '@ViewBag.CurrentUserEmail';
</script>

<form data-bind="submit:$root.save">

    <table id="tblcustomer" class="table table-bordered table-striped">
        <tr>
            <td class="c0">Customer Id</td>
            <td class="c0">
                <input type="text" id="custid" data-bind="value:$root.CustomerId" required readonly="readonly" 
class="form-control"/>
            </td>
        </tr>

        <tr>
            <td class="c0">Name</td>
            <td class="c0">
                <input type="text" id="custname" required data-bind="value:$root.CustomerName" class="form-control"/>
            </td>
        </tr>

        <tr>
            <td class="c0">Address</td>
            <td class="c0">
                <input type="text" id="custaddr" required data-bind="value:Address" class="form-control"/>
            </td>
        </tr>

        <tr>
            <td class="c0">City</td>
            <td class="c0">
                <input type="text" id="custcity" required data-bind="value:City" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Email</td>
            <td class="c0">
                <input type="email" id="custename" readonly
                       data-bind="value:Email" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Primary Contact:</td>
            <td class="c0">
                <input type="text" id="custcontact1" required data-bind="value:Contact1" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">Secondary Contact</td>
            <td class="c0">
                <input type="text" id="custcontact2" required data-bind="value:Contact2" class="form-control"/>
            </td>
        </tr>
        <tr>
            <td class="c0">
                <button id="btnsave" type="submit" class="btn btn-success">Save</button>
            </td>
            <td class="c0">
                <button id="btnsave" data-bind="click:$root.clear" class="btn btn-primary">Cancel</button>
            </td>
        </tr>
    </table>
</form>
<div data-bind="visible:$data.CustomerId()>0">
    @Html.ActionLink("Continue to Search Property", "SearchProperties",
                            "Customer")
</div>

<script src="~/Scripts/MyScripts/Customers.js"></script>

Note: The above View is bound with the ViewModel declared in Customers.js

In the above View similar to the Index.cshtml for the Owner, define a GetGlobalDeclarationCust object. This contains UserName function which accepts the current login Email from the CustomerController’s Index method. The Input elements are bound with the observables declared in the ViewModel. The view contains a <div> tag which is data bound using Visible binding with the ‘CustomerId()>0’ condition. If this condition is true, then the Action link will be visible.

In the MyScripts folder, add a new JavaScript file of the name ‘PropertySearch.js’ and add the following code in it:

(function () {
    var searchViewModel = function () {

        var self = this;
        //Array for the Property type
        self.PropertyTypes = ko.observableArray(["Flat", "Bunglaow", "Row House"]);
        //Array for the FIlter condition
        self.Filters = ko.observableArray(["AND", "OR"]);
        //Array for the property Status
        self.PropertySaleRentStatus = ko.observableArray(["Rent","Sale"]);


        self.PropertyId = ko.observable(0); //For PropertyId
        self.OwnerName = ko.observable(""); //For OwnerName
        self.Contact1 = ko.observable(); //For Primary Contact
        self.Contact2 = ko.observable(); //For Secondary Contact
        self.Email = ko.observable(""); //For Email
        self.PropertyType = ko.observable(""); //For Property Type
        self.Address = ko.observable(""); //For Address
        self.BedRoomNo = ko.observable(0);//For No of Bedrooms 
        self.TotalRooms = ko.observable(0); //For Total Rooms
        self.BuildupArea = ko.observable(0); //For Area
        self.SaleRentStatus = ko.observable(""); //For Sale or Rent
        self.Status = ko.observable(""); //For Status like Available

        self.ErrorMessage = ko.observable("");

        //Flag for selection of the PropertyType and SaleRent Status

        var propertySelected = 0, saleORrentStatusSelected = 0;

        self.SearchedProperties = ko.observableArray();

        self.selectedPropertyType = ko.observable();

        self.selectedPropertyType.subscribe(function (val) {

            if (val !== 'undefines') {
                propertySelected = 1;
                self.PropertyType(val);
            }
        });

        self.selectedSaleRentStatus = ko.observable();

        self.selectedSaleRentStatus.subscribe(function (val) {
            if (val !== 'undefined') {
                saleORrentStatusSelected = 1;
                self.SaleRentStatus(val);
            }
        });

        self.Filter = ko.observable();
        self.SelectedFilter = ko.observable("");
        self.Filter.subscribe(function (val) {
            if (val !== 'undefined') {
                self.SelectedFilter(val);
            }
        });


        //Funtion to search properties based upon the Conditions
        self.SearchProperties =function(){
            if (propertySelected === 1 && saleORrentStatusSelected === 1)
            {

                //Get the Filter Values
                propertytype = self.PropertyType();
                filter = self.SelectedFilter();
                searchtype = self.SaleRentStatus();

                var url ="/Property/"+propertytype+"/" +filter+"/" +searchtype; 
                alert(url);

                $.ajax({
                    url: url,
                    type:"GET"
                }).done(function (resp) {
                    self.SearchedProperties(resp);
                }).error(function (err) {
                    self.ErrorMessage("Error!!!!" + err.status);
                });
            }
        }

    };
    ko.applyBindings(new searchViewModel());

})();

The above JavaScript code defines the following:

  • Observable arrays for Property types, Filters and PropertySaleRentStatus.
  • The observables are declared for Owner and Owner property information.
  • The function ‘SearchProperties’ makes an Ajax call to Web API to search properties based upon the property type and Sale Rent status.

From the ‘SearchProperties()’ action method of the CustomerController,  scaffold an Empty View of the name ‘SearchProperties.cshtml’. In this view, add the following CSS, HTML Markup with DataBinding to the observable declared in the Knockout ViewModel:

@{
    ViewBag.Title = "SearchProperties";
}

<style type="text/css">
    #tblserach{
        border:double;
        width:900px;
    }
    .c0 {
      border:double;  
      width:450px;
    }

    .c1 {
      border:double;  
      width:300px;
    }
    #dvcontainer {
      width:1200px;
      height:500px;
      overflow-y:scroll;
    }
    #tblserach {
     border:double;
     width:1200px;
    }
    .c2 {
     background-color:azure;
     border:double;
     width:100px;
    }
    .c3 {
     background-color:yellow;
        border:double;
     width:100px;

    }
</style>

<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/knockout-3.3.0.js"></script>

<h2>Search Properties Available for Sale or Rent</h2>


<table id="tblserach" class="table table-bordered table-striped">
    <tr>
        <td class="c0">
            Property Type
        </td>
        <td class="c0"></td>
        <td class="c0">
            Status
        </td>
    </tr>
    <tr>
        <td class="c1">
            <select id="lstpropertytype"
                    data-bind="options:PropertyTypes,optionsCaption:'Select Property Type',value:selectedPropertyType"></select>
        </td>
        <td class="c1">
            <select id="lstfilter"
                    data-bind="options:Filters,value:SelectedFilter"></select>
        </td>
        <td class="c1">
            <select id="lststatus"
                    data-bind="options:PropertySaleRentStatus,optionsCaption:'Choose Status',value:selectedSaleRentStatus"></select>
        </td>
    </tr>
</table>
<input type="button" value="Search" id="btnsearch" 
        data-bind="click:SearchProperties" 
class="btn glyphicon glyphicon-search"/>
<div id="dvcontainer">
    <table id="tbldetails">
        <thead>
             <tr>
                 <td class="c3">Property Id</td>
                 <td class="c3">Owner Name</td>
                 <td class="c3">Primary Contact</td>
                 <td class="c3">Secondary Contact</td>
                 <td class="c3">Email</td>
                 <td class="c3">Property Type</td>
                 <td class="c3">Address</td>
                 <td class="c3">No. of BedRooms</td>
                 <td class="c3">Total Rooms</td>
                 <td class="c3">Build up Area</td>
                 <td class="c3">Sale Rent Status</td>
                 <td class="c3">Available Status</td>
             </tr>
        </thead>
        <tbody data-bind="foreach:SearchedProperties">
            <tr>
                <td class="c2">
                    <span data-bind="text:PropertyId"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:OwnerName "></span>
                </td>
                <td class="c2">
                    <span data-bind="text:Contact1"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:Contact2"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:Email"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:PropertyType"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:Address"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:BedRoomNo"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:TotalRooms"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:BuildupArea"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:SaleRentStatus"></span>
                </td>
                <td class="c2">
                    <span data-bind="text:Status"></span>
                </td>
            </tr>
        </tbody>
    </table>
</div>
<script src="~/Scripts/MyScripts/PropertySearch.js"></script>

The above View is bound with the ViewModel declared in the PropertySearch.js

In the above View:

  • The table ‘tblsearch’ contains < select > which is bound with the Observable arrays declared in the ViewModel.
  • The Button is click bind with the ‘SearchProperties’ function declared in the ViewModel.
  • · The table ‘tbldetails’ defines rows and columns which are bind with the observables declared in the ViewModel.

Step 2: Open the _Layout.cshtml file and add the following action links just below action links for Home, About, etc:

Running the Application:

Step 3: Click on the Register link, add the Email, Password and select the User Type as shown below:

i10-register

The User is registered as ‘Owner’. Now click on the Owner Link, it will look like the following:

i11-owner-create

This will display the Email of the Login user. Here enter the owner details and click on ‘Save’ as shown below:

i12-owner-details

Once the OwnerId is generated, the link ‘Continue to Register Property’ will be enabled. Once this link is clicked, the following view will be displayed:

i13-property-descriptions

This is the View where an owner can register property for Sale or Rent as shown in the following image:

i14-property-desc

Click on Save, the property id will be generated. Here if you reload the View, the details will be displayed as shown below:

i15-prop-desc

Log off here and create a new User with the Role of Customer:

i16-cust+

After registering the user as customer, click on the ‘Customer’ link to display the following view:

i17-cust-view

Add Customer Details and click on Save. The following view gets displayed:

i18-cust

The enables the ‘Continue’ to search property link. On clicking on the link, the following view gets displayed:

i19-search

From the above View, select the ‘Property Type’, Filter and Status as below:

Property Type = “Flat”, Filter = OR and Status = Rent and Click on the ‘Search’ button:

i12-search-result

This will show all search properties.

The HTML validations can be checked as follows:

i20-validations

Conclusion

In the current era of web development, client side frameworks like jQuery, Knockout, Angular get the limelight as with every new release, these libraries get more capabilities than ever before to make the user experience richer. ASP.NET MVC is built in a way to make it easier to hook any client side JavaScript library with it and work with pure HTML rather than a server-side abstraction model. Combining rich JavaScript libraries like jQuery and Knockout make ASP.NET MVC applications shine with their rich features like supporting AJAX and two-way binding.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!