Angular: Nested Directives & Scope

by pmn4

HTML

<script src="https://code.angularjs.org/1.2.9/angular.js"></script>
<div ng-controller="MyCtrl">
    <p>Choose an option:</p>
    
    
    <!-- This is the markup I want to write -->
<radio-set ng-model="obj.prop" name="obj_prop" ng-change="thanks()">
	<radio-set-button ng-value="'public'">Public</radio-set-button>
	<radio-set-button ng-value="'protected'">Protected</radio-set-button>
	<radio-set-button ng-value="'private'">Private</radio-set-button>
</radio-set>
    <!-- end: the markup I want to write -->
    
    
    
    <p class="message">Everything <em>looks</em> good, but as you can see below, changing your selection above does not updated the model below</p>
<pre>obj.prop: {{ obj.prop }}</pre>
</div>
<hr/>
<pre id="console"></pre>
<p class="message">The "name" attribute should be equal to "obj_prop"</p>

SCSS

.radio-set {
    margin: 10px;
    overflow: hidden;

	.radio-set-button {
	    display: block;
        float: left;
		background-color: #f6f6f6;

		&:hover {
			cursor: pointer;
		}

		input {
			position: absolute;
			left: -9999px;
		}
        .radio-content {
            padding: 10px;
        }
		input:checked ~ .radio-content {
			background-color: #333;
			color: #eee;
		}
	}
}
.message {
    padding: 10px;
    color: #000080;
    font-style: italic;
}

JavaScript

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

myApp
.directive("radioSet", function () {
	return {
		restrict: 'E',
		replace: true,
		scope: {
			ngModel: '=?',
			ngChange: '&',
			name: '@'
		},
		transclude: true,
		template: '<div class="radio-set" ng-transclude></div>',
		controller: function () {}
	};
})
.directive("radioSetButton", function () {
	return {
		restrict: 'E',
		replace: true,
		require: ['^radioSet', '?ngModel'],
		scope: {
			ngModel: '=?', // provided by ^radioSet?
			ngValue: '=?',
			ngChange: '&', // provided by ^radioSet?
			name: '@'      // provided by ^radioSet?
		},
		transclude: true,
		link: function (scope, element, attr) {
			element.children().eq(0).attr("name", scope.name);
		},
		template: '<label class="radio-set-button">' +
		            '<input type="radio" name="name" ng-model="ngModel" ng-value="ngValue" ng-change="ngChange()">' +
		            '<div class="radio-content" ng-transclude></div>' +
		          '</label>'
	};
});

function MyCtrl($scope) {
    $scope.obj = {
        prop: ""
    };
    $scope.thanks = function () {
        alert("Thank you for the help!");
    };
}

setTimeout(function () { // just waiting a second to allow angular to do it's thing
    var a, inputs = document.getElementsByTagName("input"), consol3;
    consol3 = document.getElementById("console");
    consol3.innerHTML += "Input attributes:";
    for (var i = 0, ct = inputs.length; i < ct; i++ ) {
        consol3.innerHTML += "\n";
        for (var j = 0, ct2 = inputs[i].attributes.length; j < ct2; j++) {
            a = inputs[i].attributes[j];
            consol3.innerHTML += "\n" + a.name + "=" + a.value;
        }
    }
}, 1000);