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 https://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:

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:

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

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

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:

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

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

Log off here and create a new User with the Role of Customer:
+
After registering the user as customer, click on the ‘Customer’ link to display the following view:

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

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

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:

This will show all search properties.
The HTML validations can be checked as follows:

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)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades 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), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn