selanac82
selanac82

Reputation: 3000

Dynamically add ng-click in dropdown directive

I'm trying to dynamically add a ng-click to a bunch of li's that get dynamically created inside of a directive. The idea is to hide the select element and overlay it with a custom div with a ul and use that as the dropdown. This way it can be styled anyway you like. I use the directive to control the select as you are selecting from the ul list. I dynamically created the ul and li's in the directive and add them before the element. I dynamically add the ng-click attribute. This all works fine, however, the ng-click never fires. So I'm forced to use the click event through jQuery (which is on the page because of Foundation). Here is my code. `dataviz.directive("customDropdown", ["$timeout", "$rootScope", "$document", "$compile", function($timeout, $rootScope, $document, $compile){

return {

    controller: function($scope) {

        $scope.clicked = function() {
            alert("ng click worked");
        }

    },

    link: function(scope, element, attributes) {

        // hide the default select element
        // TODO: use css instead
        element.hide();

        var id,
            overlay = angular.element(document.createElement("div")),
            ul = angular.element(document.createElement("ul")),
            span = angular.element(document.createElement("span"));

        id = Math.random().toString().slice(2);

        /*
         * Process the option clicked on. this will dynamically set the
         * correct option as selected and trigger the change event
         *
         * @method processOptionClick
         * @param {Element} the li element clicked on
         * @return undefined
         * */
        function processOptionClick(li) {

            if ( li.data("value") !== "0" ) overlay.addClass("active");

            span.html(li.html());

            element
                .children()
                .attr("selected", false);

            element
                .children()
                .eq(li.attr("data-value"))
                .attr("selected", true);

            // firefox and safari need you to explicitly set the selected attribute
            element[0].selectedIndex = li.attr("data-value");
            element.trigger("change");

        }

        /*
         * Populate the li based on the options in the select element
         *
         *
         * @method populateFakeDropdown
         * @return undefined
         * */
        function populateFakeDropdown() {

            // use $timeout to make sure options are populated before copying
            // them to the ul
            // TODO: this can be better. maybe a promise or custom event of something like that
            $timeout(function(){

                var options = element.children();

                // set the span html to the selected li
                span.html((element.find(":selected").html()));

                // if the value is blank then the dropdown reset so remove the active overlay
                if ( element.find(":selected").val() === "" ) overlay.removeClass("active");

                // remove all previous li's
                ul.empty();

                // loop through the options and rebuild the li's
                angular.forEach(options, function(value, key) {

                    var curOpt = angular.element(value),
                        li = angular.element(document.createElement("li"));

                    if ( curOpt.html() === "Janssen" ) overlay.addClass("active");

                    li.addClass("options");

                    // set the data-value to the index. should be the same as the option it mirrors
                    li.attr("data-value", key);
                    li.attr("data-ng-click", "clicked()");
                    li.on("click", function(){
                        processOptionClick(li);
                    });

                    // set the text of the li from the text of the option
                    li.html(curOpt.html());

                    ul.append(li);

                });


            }, 10);

        }

        span.addClass("selected");

        /*
         * set the data-id of the span. this used for when clicking on dropdown.
         * without this if you click on the text of the dropdown it wouldn't trigger
         * */
        span.attr("data-id", id);

        // hide ul
        // TODO: this should be done in the css
        ul.hide();

        overlay
            .addClass("dropdown")
            .addClass("view-height")
            .addClass(function(){
                return element.attr("class");
            });

        overlay.attr("data-id", id);
        overlay
            .append(span)
            .append(ul);

        // prepend the new overlay before the select element
        element.before(overlay);

        // bind a click event to the body. this allows us to close the "dropdown" when clicking outside of it
        angular.element($document[0].body).on("click", function(e){

            var element = angular.element(e.target);

            // hide any open ul's
            ul.hide();

            // if the element clicked on matches then show the ul
            if ( element.attr("data-id") === id ) {
                ul.addClass("active").show();
            }

        });

        // this should run only once when the directive is instatiated. it will
        // populate the fake dropdown
        populateFakeDropdown();

        // anytime there is an update to the request object update the dropdows as
        // there is a potential that they may have changed
        $rootScope.$on("updateDropdowns", function(){

            populateFakeDropdown();

        });

    }

}

}]);`

I want to use ng-click so i can keep it within angular and take advantage of ngTouch for mobile devices.

Upvotes: 0

Views: 2078

Answers (2)

selanac82
selanac82

Reputation: 3000

After playing around for a bit I relized how to compile the html. I simply have to pass in the contents of the overlay I'm using. So the line of code that worked was:

$compile(ul.contents())(scope);

Now I know how to properly use the $compile service. :)

Upvotes: 1

tymeJV
tymeJV

Reputation: 104775

You have to use $compile on new HTML that needs Angular directives:

li.html(
    $compile(curOpt.html())(scope);
);

Upvotes: 0

Related Questions