JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<div ng-app="app" ng-controller="MainCtrl as mc">
  <input-person ng-model="mc.data.person1"></input-person>
	<br/>
  <input-person ng-model="mc.data.person2"></input-person>
  <button ng-click="mc.resetPerson2()">Reset Person 2</button>
	<br/>
	<pre><code>{{ mc.data | json }}</code></pre>
</div>

CSS

* {
  box-sizing: border-box;
}

input-person {
  display: block;
}
input-person > div > label {
  display: block;
  width: 280px;
  margin-bottom: 3px;
}
input-person > div > label > span {
  display: inline-block;
  width: 120px;
}
input-person > div > label > input {
  display: inline-block;
  width: 160px;
}


input-address {
  display: block;
}
input-address > div > label {
  display: block;
  width: 280px;
  margin-bottom: 3px;
}
input-address > div > label > span {
  display: inline-block;
  width: 120px;
}
input-address > div > label > input {
  display: inline-block;
  width: 160px;
}

JavaScript

var app = angular.module('app', []);

function Person(data) {
	data = data || {};
	this.name = data.name;
	this.address = data.address ? new Address(data.address) : null;
}

function Address(data) {
	data = data || {};
	this.street = data.street;
	this.number = data.number;
}


app.controller('MainCtrl', function() {
	this.data = {
		person1: null,
		person2: new Person({
			name: 'Bob',
			address: new Address({
				street: 'Pineapple',
				number: '7'
			})
		})
	};
	
	this.resetPerson2 = function() {
		this.data.person2 = new Person({
			name: 'Bob',
			address: new Address({
				street: 'Pineapple',
				number: '7'
			})
		});
	}
});


function AbstractInputObjectController($scope) {
	this.$scope = $scope;
	this._ngModel = null;
	this.value = null;
	this._unwatch = angular.noop;
}

// abstract method; implement this!
AbstractInputObjectController.prototype.makeCopy = null;

AbstractInputObjectController.prototype.setNgModel = function(ngModel) {
	this._ngModel = ngModel;

	if( ngModel ) {
		ngModel.$render = this._render.bind(this);
	}
};

AbstractInputObjectController.prototype._makeWatch = function() {
	this._unwatch = this.$scope.$watchCollection(
		(function() {
			return this.value;
		}).bind(this),
		(function(newval, oldval) {
			if( newval !== oldval ) { // skip the initial trigger
				this._ngModel.$setViewValue(newval !== null ? this.makeCopy(newval) : null);
			}
		}).bind(this)
	);
};

AbstractInputObjectController.prototype._render = function() {
	this._unwatch();
	this.value = this._ngModel.$viewValue ? this.makeCopy(this._ngModel.$viewValue) : null;
	this._makeWatch();
};


app.directive('inputPerson', function() {

	InputPersonController.$inject = ['$scope'];
	function InputPersonController($scope) {
		AbstractInputObjectController.call(this, $scope);
	}
	
	InputPersonController.prototype = Object.create(AbstractInputObjectController.prototype);
	InputPersonController.prototype.constructor =...