I'm using Angular Treeview to build a hierarchy in my website. I've added a Bootstrap Dropdown to each node in the hierarchy, it is displayed when the user clicks on the node label.
The menu items displayed in the dropdown is different depending on the type of the node. All this I've gotten to work.
Now when the user wants to add a node a bootstrap modal is supposed to open for user input. This is where I'm stuck, the modal does not get called at all. I've gotten functions within the directive working using $(".dropdown > ul.dropdown-menu").html($compile(appendThis)(scope));, but if I want to open a model defined in the directive template it does not work.
I've tried the solutions here and here, but they are not working.
Here is a simplified version of the directive template:
template =
'<div class="modal hide fade" id="addThisNode"">'+
'<div class="modal-body">'+
'<p>This Node Body</p>'+
'</div>'+
'</div>'+
'<div class="modal hide fade" id="addOtherNode"">'+
'<div class="modal-body">'+
'<p>Other Node Body</p>'+
'</div>'+
'</div>'+
'<ul>' +
'<li data-ng-repeat="node in ' + treeModel + '">' +
'<i class="normal" '+
'data-ng-hide="node.' + nodeChildren + '.length">'+
'</i> ' +
// Call this funcion when
// the node label is clicked
'<span class="treenode {{node.' + nodeType + '}}" '+
'id="{{node.' + nodeId + '}}"'+
'data-ng-class="node.selected" '+
'data-ng-click="' + treeId +
'.selectNodeLabel(node)">'+
'{{node.' + nodeLabel + '}}'+
'</span>' +
// bootstap dropdown menu
'<div class="dropdown" data-ng-show="node.selected">'+
'<a data-toggle="dropdown"><span class="caret"></span></a>'+
'<ul class="dropdown-menu">'+
// list items get appended here
'</ul>'+
'</div>'+
'</li>' +
'</ul>';
If the user clicks the node label this function is called:
scope[treeId].selectNodeLabel = scope[treeId].selectNodeLabel || function( selectedNode ) {
// set currentNode
scope[treeId].currentNode = selectedNode;
// Get the node type
var nodetype = scope[treeId].currentNode.NodeType;
var appendThis = '';
if (nodetype == 'This'){
appendThis = '<li><a data-target="#addThisNode" href="" data-toggle="modal">Add This Node</a></li>';
}
else if (nodetype == 'Other'){
appendThis = '<li><a data-target="#addOtherNode" href="" data-toggle="modal">Add Other Node</a></li>';
}
$(".dropdown > ul.dropdown-menu").html($compile(appendThis)(scope));
};
Sorry if this is confusing, it's confusing to me as well. But if you have tips on calling modals from inside a directive anything will be appreciated.
After searching through a lot of posts I was able to find an answer. I'm posting it here in case it can help anyone else.
I changed the structure of the template a bit, but the functionality stays the same. The actual problem was in compiling the template. It worked when I compiled it like this:
$(".dropdown > ul.dropdown-menu").html(appendThis);
$compile($(".dropdown > ul.dropdown-menu").contents())(scope);
Related
I'm using angularJs. I would like to include a variable into a popover template.
angular.forEach($scope.myList, function (obj) {
obj.details = '<div uib-popover-html="' + template/template.html + '" type="button"' +
'popover-placement="bottom" name="' + obj.id+'" id="' + obj.id+'" ' +
'popover-trigger="\'click\'"> ' +
'<i class="glyphicon glyphicon-eur"></i></div>';
The template is quite simple:
<div>
<button ng-click="onClick(object.id)"></button>
</div>
What is my problem is I don't know how to link this object.id with the forEach. So, for me it is just a template with a param.
Is it possible to do?
I have and AngularJS 1.0.7 web application and I´m using AngularStrap select directive. Everything was working fine until I have moved my application to html5Mode. I did that to prettify my URLs.
But now, when I select an option in any bs-select component I´m redirected to index.
HTML
<select class="show-tick" ng-model="selectType" ng-options="boatType as (boatType.name | translate) for boatType in boatTypes" bs-select>
<option value="" selected>{{'BOAT_TYPE' | translate}}</option>
</select>
JS Controller:
$scope.$watch('selectType', function() {
if($scope.selectType != undefined) {
BoatModel.query({type_id: $scope.selectType.id},
function success(result){
if(result.length == 0){
$scope.boatModels = new Array ();
$scope.selectModel = undefined;
SearcherService.setModel(undefined);
}
else{
$scope.boatModels = result.slice();
}
}
);
SearcherService.setType($scope.selectType.id);
$scope.selectModel = undefined;
}
else {
SearcherService.setType(undefined);
$scope.selectModel = undefined;
}
});
I fixed it.
I found out the code generated by the Bootstrap Select directive is not compatible with html5Mode because it inserted an href="#" in each option in the select. When you click on it you are redirected to the index.
Original Code in the directive
createA:function(test, classes) {
return '<a tabindex="-1" href="#" class="'+classes+'">' +
'<span class="pull-left">' + test + '</span>' +
'<i class="icon-ok check-mark"></i>' +
'</a>';
I just fixed it by removing the href, like this:
createA:function(test, classes) {
return '<a tabindex="-1" class="'+classes+'">' +
'<span class="pull-left">' + test + '</span>' +
'<i class="icon-ok check-mark"></i>' +
'</a>';
I would like to create a directive to replace some code in my HTML.
Here's what I have right now:
<div class="gridFooter" ng-show="home.dataRetrieved">
<span ng-show="(home.grid.data).length">{{ (home.grid.data).length + " rows retrieved - " + home.grid.view.length + " displayed" }}</span>
<span ng-show="!(home.grid.data).length">There are no tests that match your selection criteria</span>
</div>
I created this basic directive but there are things missing:
app.directive('adminGridFooter', function () {
return {
template: '<div class="gridFooter" ng-show = "home.dataRetrieved" >\
<span ng-show = "(home.grid.data).length" >\
{{ (home.grid.data).length + " rows retrieved - " + home.grid.view.length + " displayed" }}\
</span >\
<span ng-show="!(home.grid.data).length" >xx</span >\
</div>'
};
});
How can I make it so I can pass in the string "xx" inside the element when I call the directive and will my directive just assume the current scope so that the home.dataRetrieved will work without change? Something like
Another question. Howe can I make the directivecompletely replace my call to it <admin-grid-footer></admin-grid-footer>. How can I make it so it replaces the element?
You are looking for transclude and replace:
app.directive('adminGridFooter', function () {
return {
replace:true,
transclude:true,
template: '<div class="gridFooter" ng-show = "home.dataRetrieved" >\
<span ng-show = "(home.grid.data).length" >\
{{ (home.grid.data).length + " rows retrieved - " + home.grid.view.length + " displayed" }}\
</span >\
<span ng-show="!(home.grid.data).length" ng-transclude></span >\
</div>'
};
});
PLUNKER
you can change your directive like this.
angular.module('onboardingApp').directive("adminGridFooter",function () {
return {
restrict: 'E',
link: function(scope, element, attributes) {
scope.customMessage = attributes["custommessage"];
},
templateUrl: '<div class="gridFooter" ng-show = "home.dataRetrieved" >\
<span ng-show = "(home.grid.data).length" >\
{{ (home.grid.data).length + " rows retrieved - " + home.grid.view.length + " displayed" }}\
</span >\
<span ng-show="!(home.grid.data).length" >{{customMessage}}</span >\
</div>',
};
});
then pass the value that u want in html
<admin-grid-footer customMessage="what ever you want"></admin-grid-footer>
At first, you can use templateUrl property and point it to separate HTML file instead of writing the whole HTML as a string.
The second, you can restrict the directive type by element, so you can use it only as an element (not as attribute nor as class). Here is how to do that: restrict: 'E'.
Finally, you can also specify a link function where you can get the attributes of your element and do whatever you need.
So, after these changes your code may look like this:
app.directive('adminGridFooter', function () {
return {
restrict: 'E',
templateUrl: 'adminGridFooter.html', // this contains your HTML
link: function(scope, element, attrs) {
scope.xx = attrs.xx;
}
}
});
And you can use it like this:
<adminGridFooter xx="someValue"></adminGridFooter>
And the last question:
...and will my directive just assume the current scope so that the home.dataRetrieved will work without change?
YES, by default it uses the scope where the directive was called, BUT you can filter scope variables and only use some of them, which you need inside of your directive. You can achieve this using isolated scopes.
Also, I strongly recommend to read about directives to have a basic knowledge and then continue with them.
The official documentation is a good starting point.
set the scope property to true, So the scope will be accessible in directive.
or
You can pass the data as an attribute.
app.directive('adminGridFooter', function() {
return {
restrict: 'E',
replace: true,
scope: true,
template: '<div class="gridFooter" ng-show="home.dataRetrieved" >\
<span ng-show = "home.grid.data.length > 0" >\
{{ (home.grid.data).length + " rows retrieved - " + home.grid.view.length + " displayed" }}\
</span >\
<span ng-show="home.grid.data.length === 0">There are no tests that match your selection criteria</span>\
</div>'
};
});
Or
scope:{
home:'='
}
PLUNKER
I have a requirement to render all the posts from my database by using AngularJS. I need to provide Edit functionality for each post that is visible to the user. Currently, I am doing this by using a 'edit-post' directive. Here is the linking function for that.
link: function ($scope, element, attrs) {
element.bind('click', function () {
var divId = $scope.$parent.post.meta.id + "Data";
var html = $compile("<div class='editTextAreaDiv' id='" + divId + "'>" +
"<textarea class='editTextArea' id='editBox' rows='3' ng-model='editedPostText' name='editedPostText'>" + $scope.$parent.post.meta.data + "</textarea><br />" +
"<span class='pull-right'>" +
"<input class='btn' type='button' value='Save' ng-click='saveEditedPost(\"" + divId + "\")'/>" +
"<input class='btn' type='button' value='Cancel' ng-click='cancelEdit(\"" + divId + "\")'/>" +
"</span>" +
"</div>")($scope);
$("#" + divId).html(html);
});
}
I am manipulating DOM by dynamically adding a textarea and 2 buttons.
My question is whether this approach of dynamically manipulating DOM elements is preferable in Angular world. Or should I go for some other approach (like using ng-show/ng-hide directives to show/hide the textarea and 2 buttons).
Note: I preferred not to use ng-show/ng-hide since I didn't want to introduce an extra textarea and 2 buttons for every post.
Please guide me regarding this.
I think you should use Directives.
putting your HTML inside the javascript is bad, and breaks the idea behind angular which aims to separate the logic from the view, and keep your objects lossly-coupled.
see similar question here: HTML template in AngularJS like KnockoutJS
I am new to Snecha so please bear my non technical description.
MyView.js
itemTpl: [
'<div class="pb10 font-90">{associationDiplayText}</div>' +
'<div style="float:left">' +
'<div class="bold font-90"><b>{name}</b></div>' +
'<div class="font-90">{address1}</div>' +
'<div class="font-90">{address2}</div>' +
'</div>' +
'<div style="display:block;" class="fltR" id="displayIcon">' +
'<span id="phoneId" class="phoneIcon"></span>' +
'<span id="emailId" class="emailIcon"></span>' +
'<div class="clr"></div>' +
'</div>' +
'<div class="clr"></div>' +
'<div style="display:none" id="display" class="mt15 mb10">'+
'<tpl for="contacts">',
'<div style="float:left">' +
'<div class="font-50">{contactTypeText}:</div>' +
'<div class="font-50">{name}</div>' +
'</div>' +
'<div class="fltR" id="innerdisplay">' +
'<span id="contactphone" class="phoneIcon"></span>' +
'<span id="contactemail" class="emailIcon"></span>' +
'<input type="hidden" value="{#}" id="hiddenindex" />'+
'<div class="clr"></div>' +
'</div>'+
'<div class="clr"></div>' +
'</tpl>',
'</div>'+
'<div align="center">{moreDetail}</div>'
]
Controller.js In onItemTap I am doing following
onItemTapListView: function(view, itemIndex, target, record, event, eOptions) {
if(event.getTarget("#contactphone.phoneIcon")){
var contactRecord = record.data.contacts[itemIndex];
}
The problem is that when i click the first row it gives itemIndex = 0 which is what I expect but it also gives the same result when I click on phoneIcon from contact list which has around 10,15 items. what I need is to get the index on which contact item user has clicked.
Thanks in Anticipation
Each instance of the 1st-level record object will be set to a different row no mather what it contains.
ST wont ever know on which contact is the user tapping because the entire row is a single unit.
I know the UI would be different, but my recommendation is to go with a Nested List approach.
regards-