Reputation: 3000
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
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
Reputation: 104775
You have to use $compile
on new HTML that needs Angular directives:
li.html(
$compile(curOpt.html())(scope);
);
Upvotes: 0