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'
})
});
}
});
app.directive('inputPerson', function() {
InputPersonController.$inject = ['$scope'];
function InputPersonController($scope) {
this.$scope = $scope;
this._ngModel = null;
this.value = null;
this._unwatch = angular.noop;
}
InputPersonController.prototype.setNgModel = function(ngModel) {
this._ngModel = ngModel;
if( ngModel ) {
ngModel.$render = this._render.bind(this);
}
};
InputPersonController.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 ? new Person(newval) : null);
}
}).bind(this)
);
};
InputPersonController.prototype._render = function() {
this._unwatch();
this.value = this._ngModel.$viewValue ? new Person(this._ngModel.$viewValue) : null;
this._makeWatch();
};
return {
restrict: 'E',
scope: {},
bindToController: true,
controllerAs: 'ctrl',
controller: InputPersonController,
require: ['inputPerson', 'ngModel'],
link: function(scope, elem, attrs, ctrls) {
ctrls[0].setNgModel(ctrls[1]);
},
template:
'<div>' +
'<label><span>Name:</span><input type="text" ng-model="ctrl.value.name" /></label>' +
'<input-address...