http://stackoverflow.com/questions/42821019/knockout-js-update-dirty-flag-when-any-property-changes-using-with-ko-mapping

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div>
  Description: <input type="text" data-bind="textInput: BriefDescription"/>
  <br/>  <br/>

  BulletPoints: 
  <ul data-bind="foreach: BulletPoints">
    <li data-bind="text: $data"></li>
  </ul>
  
  Tenures:
  <div data-bind="foreach: Tenures" style="border: 1px dashed orange">
    <input type="text" data-bind="textInput: Description" />
    <br/>
  </div>
  <br/>
  
  Dirty:
  <span data-bind="text: isDirty"></span>
</div>

JavaScript

var testData = {"PropertyID":0,"BulletPoints":["test 1","test 2","test 3"],"BriefDescription":"Some test desc","TakeOnAppointmentDate":"0017-03-02T12:05:00","Rooms":null,"TenureLengthOfLease":10,"TenureLengthOfService":10,"GroundCharges":11.0,"Directions":"go here then there","KeyHolderName":"Name","KeyHolderReference":"very nice","IsDisplayToPublic":true,"UploadToRightMove":false,"UploadToOnTheMarkert":false,"UploadToPrimeLocation":false,"RightMoveCost":1000.0,"EpcCost":100.0,"FloorPlanCost":2.0,"RoomIdentifier":null,"CoverIdentifier":null,"CoverPath":null,"PreviewUrl":null,"EpcReportPdf":null,"FloorPlan1":null,"FloorPlan2":null,"DisplayFrom":185.000,"DisplayTo":200.000,"SelectedSalePriceOptionId":3,"IsHidden":null,"AddressLine1":null,"NumberOfBedrooms":5,"NumberOfBathrooms":3,"NumberOfReceptionrooms":2,"Tenures":[{"Id":1,"Description":"Freehold"},{"Id":2,"Description":"LeaseHold"}],"SelectedTenureId":2,"ViewingInstructions":[{"Id":1,"Description":"Accompanied viewings with prior notice"},{"Id":2,"Description":"Vacant property"},{"Id":3,"Description":"Vendor to do viewings"}],"SelectedViewingInstructionId":2,"SaleStates":[{"Id":1,"Description":"For Sale"},{"Id":2,"Description":"Sold"},{"Id":3,"Description":"SSTC"},{"Id":4,"Description":"To Let"}],"SelectedSaleStateId":2,"UnderOffers":[{"Id":0,"Description":"No"},{"Id":1,"Description":"Yes"}],"SelectedUnderOfferId":1,"SalePriceOptions":[{"Id":1,"Description":"Asking Price"},{"Id":2,"Description":"Guide Price"},{"Id":3,"Description":"Offers Over"},{"Id":4,"Description":"In the region of"},{"Id":5,"Description":"Auction guide"}],"BlobItems":[]};

var viewModel = function(jsonData){
	var self = this;
  
  ko.mapping.fromJS(jsonData, {}, this);
  
  self.isDirty = ko.observable(false);
  self.toJS = ko.computed(function(){ return ko.toJS(self); }, this);
  self.toJS.subscribe(function(value){
  	self.isDirty(true);
  });
}

ko.applyBindings(new viewModel(testData));