Illegal use of ng-Transclude directive in the template - javascript

When I use ngTransclude, the page seems good,but got console error:
Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: {0}
I found the root cause: in index.js I used $compile to compile the modal directive.It seems $compile can't work with ng-transclude. Do you have some suggestion to fix this issue?
Here is the code
directive.js:
myApp.directive('modal', function () {
return {
restrict: 'E',
templateUrl: 'modal/modal.html',
replace: true,
transclude: true,
scope: {},
link: function(scope, element, attrs){
},
controller: ['$scope', function transcludeController($scope) {
}]
}
});
template.html:
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"> ... </div>
<div class="modal-body">
<ng-transclude></ng-transclude>
</div>
<div class="modal-footer"> ... </div>
</div>
</div>
</div>
index.html:
<modal>
<input type="text" id="test"/>
</modal>
index.js:
$scope.test = 'test';
$("#test").attr("ng-model", "test");
$compile(modal)($scope);

Please check the Plunker link. Its working there. if you can update the link with your code perhaps we can help you.
Link: https://plnkr.co/edit/ey1fXDwEOVtA64R6Io3q?p=preview

Related

How can I render compiled Transcluded HTML in Angular?

I am trying to render html that represents a custom directive using angular so that I nest divs an arbitrary number of times. When I run the below code I do see the tag properly transcluded and the browser in my output shows literally the string text " ". I would like to compile this into html and render and tried doing below with
$compile(element.contents())(scope);
However this results in console errors regarding an orphaned transclusion as well as errors saying it cannot read properties of my objects. It's my thought that this is compiling into something I do not expect it to but am not sure why or how I could see what the result is. Any ideas to get me going here? Is there a better more angular way to do this?
Module with directives
angular.module('myApp.APP', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/testapp', {
templateUrl: 'javascripts/nestedExample/testHTML.html',
controller: 'testController'
});
}])
.controller('testController', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.paneDirective = "<div pane> </div>";
}])
.directive('pane', function($compile) {
return {
scope: {},
template: "<div data-split-pane> <div data-split-pane-component data-width='33%'><p>top</p></div><div data-split-pane-divider data-width='5px'></div> <div data-split-pane-component> <ng-transclude></ng-transclude> <p>bottom</p></div></div>",
transclude: true,
link: function(scope, element, attrs) {
console.log(element.contents())
//$compile(element.contents())(scope);
},
};
});
simplified html
<div ng-controller="testController">
<div pane> {{paneDirective}} </div>
</div>
editing in formatted html code used in template
<div data-split-pane>
<div data-split-pane-component data-width='33%'>
<p>top</p>
</div>
<div data-split-pane-divider data-width='5px'></div>
<div data-split-pane-component>
<ng-transclude></ng-transclude>
<p>bottom</p>
</div>
</div>
Use Angular's ng-transclude directive in your template to specify the insertion point for the transcluded content, like so:
template: '<div ...><div ng-transclude></div> </div>'
I am not sure if this will help, but one way you can approach this is to generate your html string, then just compile the whole thing out. Any other directives you include will get compiled. Check this out and see if its workable for you...
https://plnkr.co/edit/FacA3AwOqJA2QARK28c8?p=preview
angular.module('myApp', [])
.controller('testController', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.paneDirectiveContent = `
<div data-split-pane>
<div data-split-pane-component data-width='33%'>
<p>top</p>
</div>
<div data-split-pane-divider data-width='5px'>
</div>
<sample></sample>
<div data-split-pane-component>
<p>bottom</p>
</div>
</div>
`;
}])
.directive('pane', function($compile) {
return function(scope, element, attrs) {
element.html(scope.$eval(attrs.pane));
$compile(element.contents())(scope);
}
});
Acts on this Html:
<div ng-controller="testController">
<div pane="paneDirectiveContent"></div>
</div>
And any other directives will get rendered:
.directive('sample', function($compile) {
return {
template: `<b>Other directives will be rendered</b>`
}
})

Execute a javascript function within a controller from a directive

I need to execute a javascript function that resides inside a controller. I need to call the function from within a directive.
The arguments I'm passing are fine. My method name in the controller is "GetAttachments".
When I'm debugging, and using scope, the method name GetAttachments doesn't appear.
Can someone please help me to be able to execute the named function?
Here is my directive. I need to know the proper syntax of the line: scope.GetAttachments(attrs.downloadType, attrs.downloadId). Note that my arguments are fine...
.directive('download', ['$modal', function ($modal)
{
return {
restrict: 'E',
transclude: false,
replace: true,
template: '<a style="padding-right: 5px; color:#fff !important;" class="pull-right" href="#" ng-click="opendownload()"><i class="fa fa-files-o fa-lg" style="padding-right: 5px"></i>Download</a>',
link: function (scope, elem, attrs, controller)
{
scope.opendownload = function ()
{
$modal.open({
templateUrl: root + 'AccountingModule/modal/attachment/download-modal.html',
size: 'md',
backdrop: true,
controller: 'downloadSPDocumentsController as downloadCtrl',
resolve: {
attributes: function () { return attrs; },
}
});
scope.GetAttachments(attrs.downloadType, attrs.downloadId)
}
}
}
}])
Here is my JS function inside the controller:
module.controller('downloadSPDocumentsController', ['$scope', '$http', '$modalInstance', '$location', '$window', 'attributes',
function ($scope, $http, $modalInstance, $location, $window, attributes)
{
var viewModel = this;
viewModel.attributes = attributes;
var DocumentDownloadarr;
viewModel.GetAttachments = function (CheckID, FileID)
{
Here is the HTML
<!--<p>For Testing Purpose: Download Type: {{downloadCtrl.attributes.downloadType}}</p>
<p>For Testing Purpose: ID: {{downloadCtrl.attributes.downloadId}}</p>-->
<div class="modal-header">
<h3 class="modal-title">File Download</h3>
</div>
<div class="modal-body" cg-busy="{promise:downloadCtrl.promise}">
<ul ng-init="downloadCtrl.Init()" class="list-unstyled">
<li ng-repeat="item in downloadCtrl.DocumentDownloadarr">
<div class="col-sm-12">
<div class="form-group">
<div class="col-sm-10">
<input type="text" class="form-control" ng-value="item.FileDescription" ng-readonly="true" />{{item.ExternalDocumentId}}
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-default" ng-click="downloadCtrl.DownLoadAttachment(item.ExternalDocumentId, item.FileDescription)">Download</button>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="modal-footer">
<div class=" btn-toolbar pull-right" role="toolbar">
<!--<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="downloadCtrl.GetAttachments(downloadCtrl.attributes.downloadType, downloadCtrl.attributes.downloadId)">List Attachments</button>
</div>-->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="$close()">Close</button>
</div>
</div>
</div>
You need to expose your function GetAttachments in the controller via the scope.
Instead of
viewModel.GetAttachments = function (CheckID, FileID)
try this:
$scope.GetAttachments = function (CheckID, FileID)
However please note that this will work only if the directive and the controller is sharing the scope (which I think is the case on your code). However if you want to use isolated scope (for better modularity and re-usability) of the directive , then you will have to pass a reference of the method (in your controller) to the directive's scope using '&' binding.
Take a look at this fiddler here for a demo of isolated scope and calling function in controller from directives
You might want to consider raising an event in your directive using something like:
$rootScope.$broadcast('myEventHappened');
And then in your controller listen for it with something like:
$scope.$on('myEventHappened', function() {});
This approach will keep you from tightly coupling your directive to your controller.

accessing other scope variables inside custom template

I'm trying to display files inside each folder/directory.
I'm using custom directive to disply each of the directories as follows (this part works).
But it fails to resolve the {{file}} variable inside custom template folderListing.html. Can somebody please correct me where I'm going wrong ?
folderListing.js
app.directive('folderListing', function(){
return {
restrict: 'E',
scope: {
listing:'='
},
templateUrl: 'js/directives/folderListing.html'
};
});
RdaController.js
app.controller('RdaController', ['$scope','RdaService', function($scope,RdaService) {
$scope.folders = ['RDA Server Folder','CTP Outbox', 'CTP Inbox', 'CTP Jobs'];
$scope.sendToCTP = function(file){
return RdaService.SendFileToCTP(file);
};
$scope.listOfFiles = ['learn_javascript.pdf', 'HTML Made Easy.pdf', 'AngularJS for everybody.pdf'];
}]);
index.html
<folder-listing listing="folder" ng-repeat="folder in folders"></folder-listing>
folderListing.html
<div class="row">
<div class="col-md-3" id="{{listing}}">
<table class="table table-striped">
<h3> {{ listing }} </h3>
<div class="row">
<div class="col-md-3" ng-repeat="file in listOfFiles">
{{file}}
</div>
</div>
<td><span class="btn-group" role="group"><button type="button" class="btn btn-default" ng-click="sendToCTP(file)">Execute</button></span></td>
</table>
</div>
</div>
With this:
scope: {
listing:'='
},
You have created an isolate scope passing only listing to the directive. You need to change this to:
scope: {
listing: '=',
listOfFiles: '=',
sendToCTP: '&sendToCtp'
},
To pass the function you'll have to add a send-to-ctp="sendToCTP(file)" attribute on your directive. However, in your template your button with ng-click="sendToCTP(file)" is outside your ng-repeat so file will always be undefined.

Ng-repeat in custom directive does not repeat

I'm trying really hard, but I can't find where is my problem.
I created a custom directive and it should iterate some HTML to draw the right content on the screen.
The problem is that my ng-repeat does not iterate my array. I search on stackoverflow but what I found didn't help me.
Here is my directive (it's in an external file):
app.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'view/templates/log-tab.html',
replace: true,
controller: ['$scope', 'api', function($scope, api) {
$scope.logState = false;
$scope.logData = [1,2,3,4];
$scope.loadLog = function() {
api.doRequest({
path : $scope.path,
method : "GET",
broadcast : BK_LOG
});
};
var bk = $scope.$on(BK_LOG, function(key, value){
$scope.logState = true;
console.log($scope.logData);
bk();
});
}]
};
});
And here is the directive HTML that will be rendered:
<md-tab ng-click="loadLog()" label="{{i18n['REVISIONS']}}">
<div layout="row" layout-align="center" layout-padding ng-show="!logState">
<div layout="column">
<div>
<md-progress-circular md-mode="indeterminate" md-diameter="130"></md-progress-circular>
</div>
<div>
{{i18n['LOG_LOAD']}}
</div>
</div>
</div>
<div layout="row" ng-show="logState" layout-padding>
<div layout="column">
<div ng-repeat="xyz in logData">
{{xyz}}
SOME CONTENT HERE
</div>
</div>
</div>
</md-tab>
Thanks in advance!
I removed other components from your directive code and kept it at bare minimum. The code is working fine as expected. Please see below.
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body ng-app="myApp" >
<logtab></logtab>
<script src="angular.js" type="text/javascript "></script>
<script src="app.js" type="text/javascript "></script>
</body>
</html>
app.js
var myApp = angular.module('myApp', []);
myApp.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'log-tab.html',
replace: true,
controller: ['$scope', function($scope) {
$scope.logState = false;
$scope.logData = [1,2,3,4];
console.log('hi');
}]
};
});
log-tab.html
<div>
{{logData}}
<div layout="row">
<div layout="column">
<div ng-repeat="xyz in logData">
{{xyz}}
</div>
</div>
</div>
</div>
Please use a couple of console.log's to identify the exact location. I suspect that there must be some thing wrong with the other parts of the directive or may be logState is set to false??
You can always separate the controller from the directive:
app.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'view/templates/log-tab.html',
replace: true,
scope: {
log: '='
}
};
});
Then in your controller
app.controller('MyController', ['$scope', 'api', function($scope, api) {
$scope.logTab = {
state: false,
data: [1, 2, 3, 4],
loadLog: function() {
api.doRequest({
path : $scope.path,
method : "GET",
broadcast : BK_LOG
});
}
};
var bk = $scope.$on(BK_LOG, function(key, value){
$scope.logTab.state = true;
console.log($scope.logTab.data);
bk();
});
}]);
Then change your HTML to:
<md-tab ng-click="log.loadLog()" label="{{i18n['REVISIONS']}}">
<div layout="row" layout-align="center" layout-padding ng-show="!log.state">
<div layout="column">
<div>
<md-progress-circular md-mode="indeterminate" md-diameter="130"></md-progress-circular>
</div>
<div>
{{i18n['LOG_LOAD']}}
</div>
</div>
</div>
<div layout="row" ng-show="log.state" layout-padding>
<div layout="column">
<div ng-repeat="xyz in log.data">
{{xyz}}
SOME CONTENT HERE
</div>
</div>
</div>
</md-tab>
And call it with
<logtab log="logTab"></logtab>
Updated my answer based on OP's explanation.
Created a fiddle here: https://jsfiddle.net/frishi/bzbbo5da/14/
I simplified the directive definition a little, removed the api part of it.
I also changed the directive's template to include the ng-repeat part of it.
//....
restrict: 'E',
template: `<p><div ng-repeat="xyz in logData">
{{xyz}}
SOME CONTENT HERE
</div></p>`,
replace: false,
scope: true, // <- needs to be set
//....
You are missing the scope parameter in the directive definition object. You have to set it to scope: true for your directive's template to be able to access a variable defined on the directive controller's scope.

Issue with Multi Transclude in Angular

I am using multitranscldue in angular js.I am having a directive like below.
(function(){
angular.module("MyDirective",['multi-transclude'])
.directive("myDirective",function(){
var MyLink = function(scope,element,attr){
scope.title = attr.title;
}
return{
restrict : 'EA',
scope: {title:"="},
transclude:true,
templateUrl:"directive.html",
link: MyLink
}
});
}())
The html for directive is like
<div >
<div>
<span data-ng-bind="title"></span>
<div ng-multi-transclude="card-body"></div>
</div>
</div>
In my main html class, I am using the directive like below.
<my-directive title="asdasdas">
<div name="card-body">
{{title}}
</div>
</my-directive>
I am getting an error like
Error: Illegal use of ngMultiTransclude. No wrapping controller.
Please help me with this.
The link to plunker is here
you have to add ng-multi-transclude-controller to your directive's HTML template, as follows:
<div ng-multi-transclude-controller>
<div>
<span data-ng-bind="title"></span>
<div ng-multi-transclude="card-body"></div>
</div>
</div>

Categories

Resources