How to use $compile inside an Angular directive?

I have a collection of 500 items (persons) that I am rendering in an Angular ng-repeat directive. Each item has three fields (FirstName, LastName, Company). I want the user to be able to see details / edit the items below each rendered row. I have a button (Font Awesome square-plus) that when clicked needs to show the item details / edit. I do not want to include this markup/logic within the controller because having it rendered but hidden is very slow...multiple seconds in Chrome. I assume this is due to all the watches.

To resolve the issue, I created a directive that injects the details / edit items under the current record at run-time. I attempt to $compile the markup to link it to the current scope of the ng-repeat row.

Problems.. I think I am having issues with the scope. The directive is references within the ng-repeat block (p in Persons), so I would think I would be passed the record scope in the directive link function. But I can only get the record object by getting the parent scope (scope.$parent.p instead of scope.p). I don't understand.

Also, once the $compile function is executed, I do see the person info displayed in the new details blocks. But changes are not reflected in the current record data, nor an I dismiss the details block.

Any suggestions?

Markup:

<div class="row" ng-repeat="p in Persons">
    <div class="col-lg-1">
        <i class="fa fa-plus-square" ng-show="!p.showDetail" manage-details></i>
    </div>
    <div class="col-lg-2">{{::p.FirstName}}</div>
    <div class="col-lg-2">{{::p.LastName}}</div>
    <div class="col-lg-2">{{::p.Company}}</div>
    <div id="marker{{$index}}"></div>
    <hr ng-if="!$last" />
</div>

JS:

(function () {

'use strict';

angular
    .module('ngRepeatMystery', [])
    .controller('TestController', TestController)
    .directive('manageDetails', manageDetails);

TestController.$inject = ['$scope'];

function TestController($scope) {

    $scope.Persons = [
        { 'FirstName': 'Joe', 'LastName': 'Delbridge', 'Company': 'Dow', 'showDetail': false },
        { 'FirstName': 'Tony', 'LastName': 'Ingram', 'Company': 'Samtec', 'showDetail': false },
        { 'FirstName': 'Mike', 'LastName': 'Smolinski', 'Company': 'HCHB', 'showDetail': false },
        { 'FirstName': 'Lee', 'LastName': 'Shipman', 'Company': 'Cummins', 'showDetail': false },
        { 'FirstName': 'Eric', 'LastName': 'ONeal', 'Company': 'MSD', 'showDetail': false },
    ];

    $scope.DismissDetails = function (index) {

        $scope.Persons[index].showDetail = false;

        var wrappedMonkey = angular.element($document[0].getElementById('details' + index));
        angular.element(wrappedMonkey).hide();
    }
};

manageDetails.$inject = ['$compile', '$document', '$timeout'];

function manageDetails($compile, $document, $timeout) {

    return {
        restrict: 'A',
        scope: {},
        link: function (scope, element, attrs) {

            element.bind('click', function () {

                // scope.$parent !!?  WAT!
                scope.$parent.p.showDetail = !scope.$parent.p.showDetail;

                if (scope.$parent.p.showDetail) {

                    var index = scope.$parent.$index;

                    var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
                    var details = getDetailsTemplate(index);
                    wrappedMarker.replaceWith(details);

                    var wrappedDetails = angular.element($document[0].getElementById('details' + index));


                    $compile(wrappedDetails.contents())(scope.$parent);

                };
            });
        }
    };

    function getDetailsTemplate(index) {
        var detailsTemplate =
        "<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
                "<div class=\"row\">" +
                    "<div class=\"col-lg-2\"></div>" +
                    "<div class=\"col-lg-8\">" +
                        "<label>Last Name</label>" +
                        "<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
                        "<label>First Name</label>" +
                        "<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
                        "<label>Company</label>" +
                        "<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
                        "<button class=\"btn btn-primary\" ng-click=\"p.DismissDetails($index);\">Done</button><br/>" +
                    "</div>" +
                    "<div class=\"col-lg-2\"></div>" +
                "</div>" +
            "</div>";

        return detailsTemplate;
    }
};
})()

Plunker: http://plnkr.co/edit/64TcuhaNi2JcC1hzon15

I am also open to other alternatives...

Ok, I think there are a lot of issues with your code.

I would advise against having the directive modify something outside of itself.

As I commented earlier, don't use $parent. Just pass data as attributes. And create a new scope when you call $compile to avoid polluting the existing scope.

I modified your code so it works, but it's still not pretty:

http://plnkr.co/edit/eLNxewwFzobqTkQ4867n

HTML:

<div class="row" ng-repeat="p in Persons">
    <div class="col-lg-1">
        <i class="fa fa-plus-square" ng-show="!showDetail" manage-details monkey="p" index="$index" show-detail="showDetail"></i>
    </div>
    <div class="col-lg-2">{{p.FirstName}}</div>
    <div class="col-lg-2">{{p.LastName}}</div>
    <div class="col-lg-2">{{p.Company}}</div>
    <div id="marker{{$index}}"></div>
    <hr ng-if="!$last" />
</div>

JS:

    return {
        restrict: 'A',
        scope: {
          monkey: '=',
          index: '=',
          showDetail: '='
        },
        link: function (scope, element, attrs) {
            var childScope;

            element.bind('click', function () {

                scope.showDetail = !scope.showDetail;

                if (scope.showDetail) {
                    childScope && childScope.$destroy();
                    childScope = scope.$new();

                    var index = scope.index;

                    var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
                    wrappedMarker.html(getDetailsTemplate(index));

                    childScope.p = angular.copy(scope.monkey);

                    childScope.dismissDetails = function () {
                        scope.showDetail = false;
                        scope.monkey = angular.copy(childScope.p);
                        wrappedMarker.html('');
                    };

                    $compile(wrappedMarker.contents())(childScope);
                };
            });
        }
    };

    function getDetailsTemplate(index) {
        var detailsTemplate =
        "<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
                "<div class=\"row\">" +
                    "<div class=\"col-lg-2\"></div>" +
                    "<div class=\"col-lg-8\">" +
                        "<label>Last Name</label>" +
                        "<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
                        "<label>First Name</label>" +
                        "<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
                        "<label>Company</label>" +
                        "<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
                        "<button class=\"btn btn-primary\" ng-click=\"dismissDetails();\">Done</button><br/>" +
                    "</div>" +
                    "<div class=\"col-lg-2\"></div>" +
                "</div>" +
            "</div>";

        return detailsTemplate;
    }