Elem bind not working when transclude is set to true - javascript

I have been trying to transclude content of an element, and manipulate it's display property by binding click event to one of the children of the element. But it doesn't seem to be working.
app.directive('apple', function(){
function link(scope, elem, attr, ctrl, transclude) {
elem('a.link').bind('click', function() {
('ul.menu').toggleClass('active');
});
transclude(scope, function(clone) {
elem.html(clone);
});
}
return {
restrict: 'E',
transclude: true,
link: link
};
});
HTML:
<apple>
<a class="link" href="#">Show</a>
<ul class="menu">
<li>linky</li>
</ul>
</apple>
Any idea what's going on? link to fiddle: http://jsfiddle.net/pb2q4zj4/1/

Just in case you're really trying to make a drop down menu, rather than working out how transclude works, here's a simpler way;
View
<div ng-app ng-init="show=false">
<div>
<a ng-click="show = !show">Show</a>
<ul ng-show="show">
<li>linky</li>
</ul>
</div>
</div>
JS
angular.module('app', []);
http://jsfiddle.net/4oq1zLsg/

Related

ng-bind-html renders data as HTML but ignores children elements

I'm working on this function that generates HTML based on user input and I can render it as proper HTML inside a list item (instead of a string version) using ng-bind-as-html.
The only problem is that it doesn't render the span tag that is also a child element in the li tag. I'm an Angular n00b and could use any insight ya got.
The code below allows me to have my functions output as proper HTML, which is nice, but I need that span tag to show up as it allows my user to copy the contents of the text.
Basically, I can either rewrite without ng-bind-html and have the span render appropriately, or I can have my HTML output render and not get the span tag. I'm stuck with one or the other and not both... and I want both. Classic.
Thanks for your help!
<li
class="entry"
ng-repeat="entry in output track by $index"
ng-mouseenter="onEnter()"
ng-bind-html="entry">
{{entry}}
<span
class="copy"
ng-click="copyData(entry)"
ng-mouseenter="onEnter()">
{{message}}
</span>
</li>
You can create a custom directive to achieve this with transclude and ng-transclude as below.
Documentation for ng-transclude
var app = angular.module('app', []);
app.controller('TestController', ['$scope', function($scope) {
$scope.message = 'You are welcome!';
$scope.output = ['<h1>Hello user</h1>'];
}]);
app.directive('bindHtml', ['$sce', function($sce) {
return {
restrict: 'A',
transclude: true,
template: '<span><span ng-bind-html="html"></span><span ng-transclude></span></span>',
link: function (scope, element, attrs) {
scope.html = $sce.trustAsHtml(scope.$eval(attrs.bindHtml));
}
};
}]);
angular.bootstrap(document, ['app']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="TestController">
<li
class="entry"
ng-repeat="entry in output track by $index"
ng-mouseenter="onEnter()"
bind-html="entry">
<span
class="copy"
ng-click="copyData(entry)"
ng-mouseenter="onEnter()">
{{message}}
</span>
</li>
</div>

watch controller scope variable from a sidebar directive

I have a sidebar directive in my web application with a set of values that bind to some model in another controller. When page loads, all the values are fetched and populated correctly. However when the model is updated from the controller, the event is not captured by the watch setup in directive.
Here is my sidebar html:
<div class="navbar-default sidebar" ng-controller="SettingsCtrl" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav in" id="side-menu" style="padding-left: 8px;">
<li ng-repeat="profile in profiles">
<label>
<input type="radio" ng-model="profile" name="name" value="{{profile.text}}"/>
{{profile.text}}
</label>
</li>
</ul>
</div>
</div>
Directive sidebar.js:
angular.module('webApp')
.directive('sidebar',['$location',function(Request) {
return {
templateUrl:'scripts/directives/sidebar/sidebar.html',
restrict: 'E',
replace: true,
scope: false,
link: function(scope, element, attrs){
scope.$watch('profiles', function(newValue, oldValue){
console.log(newValue + ' ' + oldValue);
}, true);
}
}
}]);
Here is my SettingsCrl controller:
angular.module('webApp')
.controller('SettingsCtrl', function($scope) {
$scope.profiles = [{text: "john"}, {text: "paul"}];
function someEvent(){
$scope.profiles.push({text: "hannah"});
}
});
I have tried to do watchCollection instead, but still no luck..

how to use jQuery plugin (semantic-ui) in angularJS directive?

I use semantic-ui in my project, the pulgin is checkbox
someone say if use jQ plugin, you must use it in angular directive
but it doesn't work
the checkbox of semantic-ui setting in semantic-ui API document, you must set this to init checkbox
$('.ui.checkbox').checkbox();
I try to change it to angular like this:
app.html
<div class="ui animate list">
<div class="item ui toggle checkbox" todo-checkbox ng-repeat="item in day track by $index">
<input type="checkbox">
<label ng-bind="item.content"></label>
</div>
</div>
and this is directive in angularjs file
todoApp.directive('todoCheckbox', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
$(elem).checkbox();
}
};
});
but it doesn't work in my project. Why?
You're close. elem is the element of the directive. In this case it is
<div class="item ui toggle checkbox" todo-checkbox ng-repeat="item in day track by $index">
<input type="checkbox">
<label ng-bind="item.content"></label>
</div>
Now, if we use find to help us locate the input field within the elem, then we can select the input field and run the checkbox method.
todoApp.directive('todoCheckbox', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
angular.forEach(elem.find( "input" ), function(inputField) {
inputField.checkbox();
}
}
};
});
A bit late to the party but you should be able to just move the todo-checkbox to the input tag (same with the semantic ui specific attributes)
Try
angular.element(elem).checkbox();
instead of
$(elem).checkbox();

Directive link function does not have access to the entire template DOM

I have a directive which has a template that recursively include a template. In my directive link function, I am unable to get the complete DOM with a selector.
Here is my directive. Notice that my directive try to call dropdown() function on all .ui.dropdown divs constructed so nested dropdown will be activated.
.directive("floatingDropdown", function() {
return {
restrict: 'E',
templateUrl: "scripts/Ui/FloatingDropdown.html",
replace: true,
scope: {
uiClass: '#',
model: '=ngModel',
optionTree: '='
},
link: function(scope, elem, attrs) {
scope.elemClass = scope.uiClass || "ui floating dropdown icon button";
$(elem).dropdown();
$(elem).find(".ui.dropdown").dropdown();
}
}
})
The scripts/Ui/FloatingDropdown.html contains a nested include. This creates multiple levels of dropdowns
<div class="{{elemClass}}">
<script type="text/ng-template" id="node_template.html">
<div class="ui dropdown" ng-if="option.options">
<span ><i class="dropdown icon"></i> {{option.value}}</span>
<div class="menu" ng-if="data.options">
<div class="item" ng-repeat="option in data.options" ng-include="'node_template.html'"></div>
</div>
</div>
<span ng-if="!option.options" ng-click="model=option">{{option}}</span>
</script>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" ng-repeat="option in optionTree.options" ng-include="'node_template.html'">
</div>
</div>
</div>
My problem is $(elem).find(".ui.dropdown") will not find the recursively generated divs by ng-include
By attempting to do DOM manipulation in a directive's link() method like that, you're trying to query/modify a part of the DOM that hasn't been rendered yet.
You need to defer those jquery calls until later. You can do this using:
$scope.$evalAsync(function() {
// DOM code
});
or
$timeout(function() {
// DOM code
}, 0);
Using $evalAsync will run the expression during the next $digest cycle, will allow you to modify HTML before it's rendered in the browser. Using $timeout will wait until all $digest cycles are complete.

angularjs: how does the replace attribute works for directive ?

I'm trying to figure out how the 'replace' attribute work for directive. I've run into a scenario when setting it to true was causing my code to break.
The directive:
angular.module('cdt.dm.directives').directive('referenceFiles', [
function () {
return {
restrict: 'E',
templateUrl: 'app/dm/views/templates/referenceFiles/referenceFiles.html',
scope: {
job: '='
},
link: function (scope, element, attr) {
scope.deleteReferenceFile = function (id) {
scope.job.references.splice(id, 1);
}
}
}
}]);
the referenceFiles.html template:
<div class="grid-action-filter" popover-placement="left" popover-template="app/dm/views/templates/referenceFiles/simple.html">
<span class="badge" style="cursor:pointer" >{{job.references.length}} added</span>
the simple.html template used by the popover directive:
<span>{{job.references.length}} reference files</span>
<table ng-repeat="ref in job.references">
<tr>
<td>{{ref.name}}</td>
<td>
<button class="btn grid-button btn-danger" ng-click="deleteReferenceFile($index);"><i class="fa fa-trash-o"></i></button>
</td>
</tr>
</table>
If I set replace to true in the referenceFiles directive, then the deleteReferenceFile method won't be found on the scope when clicking the button. Instead I have to call it this way:
$parent.$parent.$parent.$parent.deleteReferenceFile($index)
needless to say, it's ugly...
If I remove the replace attribute of the directive, then everything works fine.
Could someone explain this behaviour ?

Categories

Resources