Partial View does not compile properly in angular - javascript

// Code goes here
var mymodule = angular.module('myapp', []);
mymodule.controller('mycontroller', function ($scope) {
});
mymodule.directive('pvTempUrl',
function ($http, $compile, $log, $templateCache) {
$log.info("Directive Called");
return {
restrict: 'A',
replace: true,
compile: function (telement, tattr, transclude) {
var templateloader = $http.get(tattr.pvTempUrl, { cache: $templateCache }).
success(function (data) {
$log.info("Success-" + data);
telement.html(data);
}).
error(function (data, status) {
$log.warn("Error occured - " + data + " status-" + status);
});
return function (scope, element, attr) {
templateloader.then(function () {
var compiledHtm = ($compile(telement.html())(scope)).html();
$log.info("compiled html-" + compiledHtm);
element.html(compiledHtm);
});
};
}
};
});
I have a partial page trying to compile the page is working just showing the template as such.
Plunkr is avaliable here
http://plnkr.co/edit/U85rmXhuQGKx5pkzUu99?p=preview
Issue found i was binding the html not the compiled object resolved the issue like below
// Code goes here
var mymodule = angular.module('myapp', []);
mymodule.controller('mycontroller', function ($scope) {
});
mymodule.directive('pvTempUrl',
function ($http, $compile, $log, $templateCache) {
$log.info("Directive Called");
return {
restrict: 'A',
replace: true,
compile: function (telement, tattr, transclude) {
var templateloader = $http.get(tattr.pvTempUrl, { cache: $templateCache }).
success(function (data) {
$log.info("Success-" + data);
telement.html(data);
}).
error(function (data, status) {
$log.warn("Error occured - " + data + " status-" + status);
});
return function (scope, element, attr) {
templateloader.then(function () {
var compiledHtm = ($compile(telement.html())(scope));
$log.info("compiled html-" + compiledHtm);
//element.html(compiledHtm);
element.replaceWith(compiledHtm);
$log.info(element.html());
});
};
}
};
});

I haven't seen anyone try to load a template this way before. The proper Angular ways of loading a template are the following:
Using ng-view to load a predefined template file.
Building your own custom directive.
I feel like you are attempting to do the second method. The following code sample is an example of how to do that.
angular.module("myapp")
.directive('buttonsRadio', function() {
return {
restrict: 'E',
scope: false,
controller: function($scope){
//Any logic required for directive to work.
},
templateUrl: 'template.html'
};
})
In your template.html file you would have your template you wish to load.
For much more detail and examples on how to build your own custom directive elements, see the section Writing Directives (long version) in the Angular docs.
UPDATE: Edited to show example of templateUrl.

Related

How to set directive dynamically by data from $scope in angular-ui-grid

Sorry my english is poor ... Don't laugh, please...
In compile, I can't use $scope.
When I set directive (For example, 'ui-grid-selection') by hard code, It works perfectlly like enter image description here
In link, when I set attribute 'ui-grid-selection' only, it doesn't work.
When I set attr and write $compile($element.find('.mygrid'))($scope), it works but only the function is correct, it's layout became like this and the console log shows:
Error: [ngTransclude:orphan] http://errors.angularjs.org/1.3.20/ngTransclude/orphan?p0=%3Cdiv%20ng-transclude%3D%22%22%3E
at Error (native)
at http://localhost:8000/bower_components/angular/angular.min.js:6:417
at Ia.link (http://localhost:8000/bower_components/angular/angular.min.js:239:272)
at Z (http://localhost:8000/bower_components/angular/angular.min.js:70:149)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:255)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:352)
at http://localhost:8000/bower_components/angular/angular.min.js:50:444
at http://localhost:8000/bower_components/angular/angular.min.js:52:322
at h (http://localhost:8000/bower_components/angular/angular.min.js:56:322) <div ng-transclude="">
Error: [$compile:ctreq] http://errors.angularjs.org/1.3.20/$compile/ctreq?p0=uiGrid&p1=uiGridHeaderCell
at Error (native)
at http://localhost:8000/bower_components/angular/angular.min.js:6:417
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:127)
at http://localhost:8000/bower_components/angular/angular.min.js:56:193
at r (http://localhost:8000/bower_components/angular/angular.min.js:7:322)
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:170)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:104)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
at http://localhost:8000/bower_components/angular/angular.min.js:50:444
at http://localhost:8000/bower_components/angular/angular.min.js:52:322
Error: [$compile:ctreq] http://errors.angularjs.org/1.3.20/$compile/ctreq?p0=uiGrid&p1=uiGridRow
at Error (native)
at http://localhost:8000/bower_components/angular/angular.min.js:6:417
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:127)
at http://localhost:8000/bower_components/angular/angular.min.js:56:193
at r (http://localhost:8000/bower_components/angular/angular.min.js:7:322)
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:170)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:104)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:199)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
Error: [$compile:ctreq] http://errors.angularjs.org/1.3.20/$compile/ctreq?p0=uiGrid&p1=uiGridRow
at Error (native)
at http://localhost:8000/bower_components/angular/angular.min.js:6:417
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:127)
at http://localhost:8000/bower_components/angular/angular.min.js:56:193
at r (http://localhost:8000/bower_components/angular/angular.min.js:7:322)
at M (http://localhost:8000/bower_components/angular/angular.min.js:56:170)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:104)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
at I (http://localhost:8000/bower_components/angular/angular.min.js:59:199)
at g (http://localhost:8000/bower_components/angular/angular.min.js:51:335)
I don't know how to fix it.Sad me, Help please ...
Here my controller's code:
'use strict';
app.controller('mainCtrl',
['$scope', '$q', '$http', 'uiGridTreeViewConstants', function ($scope, $q, $http, uiGridTreeViewConstants) {
$scope.gridOptions1 = {
onRegisterApi: function( gridApi ) {
$scope.grid1Api = gridApi;
}
};
$http.get('demo/data/uigrid_100.json').success(function(data) {
$scope.gridOptions1 = data.gridOptions;
$scope.gridEnable1 = data.gridEnable;
$scope.gridOptions1.data = data.gridData;
});
}]);
app.directive('jsonUiGrid', ['$compile', function($compile) {
return {
restrict: 'E',
template:
'<div>' +
'<div ui-grid="options" class="mygrid"></div>' +
'</div>'
,
replace: true,
transclude: true,
scope: {
options: '=?',
enablesData: '=enables'
},
link: function($scope, $element, attrs){
$scope.$watch('enablesData', function(newValue) {
if (typeof(newValue) != 'undefined'){
if(newValue && newValue.length){
angular.forEach(newValue, function(attr){
$element.find('.mygrid').attr(attr, '');
});
$compile($element.find('.mygrid'))($scope);
}
}
});
}
}
}])
Tried get data by using $http before run mainCtrl, And Inject into mainCtrl. It works!!
The detail is. I'm using a template named "Clip-Two_130", it contains a function in "config.router.js" which is named "loadSequence".
function loadSequence() {
var _args = arguments;
return {
deps: ['$ocLazyLoad', '$q',
function ($ocLL, $q) {
var promise = $q.when(1);
for (var i = 0, len = _args.length; i < len; i++) {
promise = promiseThen(_args[i]);
}
return promise;
function promiseThen(_arg) {
if (typeof _arg == 'function')
return promise.then(_arg);
else
return promise.then(function () {
var nowLoad = requiredData(_arg);
if (!nowLoad)
return $.error('Route resolve: Bad resource name [' + _arg + ']');
return $ocLL.load(nowLoad);
});
}
function requiredData(name) {
if (jsRequires.modules)
for (var m in jsRequires.modules)
if (jsRequires.modules[m].name && jsRequires.modules[m].name === name)
return jsRequires.modules[m];
return jsRequires.scripts && jsRequires.scripts[name];
}
}]
};
}
And I set router like:
.state('app.demo_ui-grid2', {
url: "/demo_ui-grid2",
templateUrl: "demo/views/demo_ui-grid2.html",
//resolve: loadSequence('ui.grid', 'ui.grid.selection', 'demoUiGridCtrl2'),
resolve: {
deps: loadSequence('ui.grid', 'ui.grid.selection', 'demoUiGridCtrl2').deps,
options: function($q, $http){
var deferred = $q.defer();
$http.get('demo/data/uigrid_options.json').then(function(data) {
deferred.resolve(data.data);
});
return deferred.promise;
}
},
controller: 'mainCtrl',
title: 'demo_ui-grid2',
ncyBreadcrumb: {
label: 'demo ui-grid2'
}
})
The controller file has been modified to :
'use strict';
app.controller('mainCtrl',
['$scope', '$q', '$http', 'options', 'uiGridConstants', 'uiGridData',
function ($scope, $q, $http, options, uiGridConstants, uiGridData) {
// Bind grid enable
$scope.gridEnable1 = options.gridEnable;
// Bind grid options
$scope.gridOptions1 = options.gridOptions;
$scope.gridOptions1.onRegisterApi = function(gridApi){
$scope.grid1Api = gridApi;
};
// Bind grid data [NO NECESSARY]
//$scope.gridOptions1.data = data;
$scope.url = [
'demo/data/100.json',
'demo/data/500_complex.json'
];
$scope.changeData = function(url){
var promise = uiGridData.get(url);
promise.then(function(data) {
$scope.gridOptions1.data = data;
}, function(data) {
console.log('changeData ERROR');
});
}
}]);
app.factory('uiGridData', ['$http', '$q', function ($http, $q) {
return {
get: function(url) {
var deferred = $q.defer();
$http({method: 'GET', url: url}).
success(function(data, status, headers, config) {
deferred.resolve(data);
}).
error(function(data, status, headers, config) {
deferred.reject(data);
});
return deferred.promise;
} // end get
};
}]);
app.directive('jsonUiGrid', ['$compile', function($compile) {
return {
restrict: 'E',
template:
'<div>' +
'<div ui-grid="options" class="mygrid"></div>' +
'</div>'
,
replace: true,
transclude: true,
scope: {
options: '=?',
enablesData: '=enables'
},
link: function($scope, $element, attrs){
angular.forEach($scope.enablesData, function(attr){
$element.find('.mygrid').attr(attr, '');
})
$element.html($element.html());
$compile($element.contents())($scope);
}
}
}])
Maybe I can only modify the controller file without modifying in router file?
Wish there's a better solution. Still need help.
The data file you can use:
100.json
500_complex.json

Encapsulated Link and Controller in Directive

I wan't to program a flexible angular directive with it's properties defined in an own, simple object.
Angular:
contentFactory.directive("listViewDir", function ($compile) {
return {
restrict: "E",
scope: {
datasource: '=',
config: '='
},
controller: function ($scope) {
return $scope.config.controller($scope);
},
link:
return $scope.config.link(scope, element, attrs);
}
}
});
Own Configuration Object:
contentFactory.controller("indexCtrl", function ($scope) {
$scope.config = oLiftTabs;})
var configurations = [{
controller: function ($scope) {
$scope.ButtonClicked = function () {
alert('Button wurde geklickt!');
}
return $scope;
},
link: function (scope, element, attrs){
var template = "... myTemplate ..";
element.html(template);
$compile(element.contents())(scope);
},
}]
While my solution for the controller works well, it doesn't for the link.
Is there a more proper way for my approach? Can I realize access in my encapsulated method to the services (like $compile) without declaring it in the directive declaration?
Is this what you're trying to achieve? You didn't make it clear where you expect this object to live so I've assumed you want it a parent controller. This doesn't feel like a good idea but without knowing more about your use case it's hard to say.
DEMO
html
<body ng-controller="MainCtrl">
<list-view-dir config="config"></list-view-dir>
</body>
js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $compile) {
$scope.config = {
controller: function ($scope) {
$scope.ButtonClicked = function () {
alert('Button wurde geklickt!');
}
return $scope;
},
link: function (scope, element, attrs){
var template = '<button ng-click="ButtonClicked()" > Alert</button>';
element.html(template);
$compile(element.contents())(scope);
},
};
});
app.directive("listViewDir", function(){
return {
restrict: "E",
scope: {
datasource: '=',
config: '='
},
controller: function ($scope) {
return $scope.config.controller($scope);
},
link: function(scope, element, attrs){
return scope.config.link(scope, element, attrs);
}
};
});
Update
From your comments it sounds like you need to use a factory. Maybe something like this? It feels pretty ugly but it could be what you're looking for.
DEMO2
var app = angular.module('plunker', []);
app.factory('directiveConfigurations', function($compile){
var configurations = {
'listViewDir': {
controller: function ($scope) {
$scope.ButtonClicked = function(){
alert('Button wurde geklickt!');
};
return $scope;
},
link: function (scope, element, attrs){
var template = '<button ng-click="ButtonClicked()" > Alert</button>';
element.html(template);
$compile(element.contents())(scope);
}
}
};
return {
get: get
};
////////////////////////
function get(key){
return configurations[key];
}
});
app.controller('MainCtrl', function($scope, directiveConfigurations) {
$scope.config = directiveConfigurations.get('listViewDir');
});
app.directive("listViewDir", function(){
return {
restrict: "E",
scope: {
datasource: '=',
config: '='
},
controller: function ($scope) {
return $scope.config.controller($scope);
},
link: function(scope, element, attrs){
return scope.config.link(scope, element, attrs);
}
};
});

angularjs templates and css from directive folder

I would like to know how attach css and templates url in a angularjs directive.
For example, with templates i have this
/*** myDirectives ***/
angular.module('myDirectives', [])
.directive('webcams', function($sce, $http, $templateCache, $compile) {
return {
restrict: 'A',
template: '<div class="included" ng-include src="getTemplateUrl();"></div>',
scope: {
stream_webcam: '=webcam',
stream_type: '#type'
},
controller: function($scope, $sce) {
$scope.getTemplateUrl = function() {
var url;
if ($scope.stream_webcam.webcam_domain == 'twitcam') {
url = 'templates/' + $scope.stream_webcam.webcam_domain + '.php';
} else if ... {
url = 'templates/second.html';
} else ...
return url;
}
...
How to put templates in my angularjs folder directive ?
Pass HTML template file to directive
you can pass by templateUrl . you can give CSS file link in your html file itself as usual.
app.directive('contactTable', ['$window',,
function($window) {
return {
scope: {
contact: "=",
},
restrict: 'A',
controller: 'contactTableController',
templateUrl: **your folder**+ '/contact-table-template.html',
link : function ($scope, element, attrs){
//your logic and dom access goes here
});
}
};
}
]);

angular directive load after got variables

I have an angular directive that loads data from service,
BUT
it loads the data with a variable he go from a controller that it was loaded as well from a service.
code:
directive:
app.directive("posts", ['Posts', function(Posts) {
return {
restrict: 'E',
template: '' +
'<div ng-repeat="post in posts"></div>',
scope: {
showLoading: '&',
hideLoading: '&',
spot: '#'
},
controller: ['$scope', function ($scope) {
}],
link: function(scope, element, attrs) {
$scope.load = function () {
Posts.loadPostsBySpot(scope.spot)
};
}
};
}]);
Controller
app.controller('spotPageController', ['$scope', 'Spots', function ($scope, $Spots) {
doit = function () {
Spots.getSpot($)
.success(function (data) {
$scope.spotId = data.data;
console.log($scope.spot);
}).error(function (data) {
console.log('error');
});
};
}]);
html inside
<posts spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
but when the directive is loaded the "spot" is not yet set,
so how do I make the directive load only after the spot is set.
Use ng-if.
<posts ng-if="spotId" spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
This element will be rendered only after the spotId is initialized. Therefore, your directive will not be called before that.
If you want to encapsulate this behavior in directive, you should watch for changes of the scopeId. See the fiddle.

AngularJs Directive with dynamic Controller and template

I want to create a directive that has dynamic view with dynamic controller. the controller and the template view is coming from the server.
The Directive
var DirectivesModule = angular.module('BPM.Directives', []);
(function () {
'use strict';
angular
.module('BPM.Directives')
.directive('bpmCompletedTask', bpmCompletedTask);
bpmCompletedTask.$inject = ['$window'];
function bpmCompletedTask ($window) {
// Usage:
// <bpmCompletedTask></bpmCompletedTask>
// Creates:
//
var directive = {
link: link,
restrict: 'E',
scope: {
type: '=',
taskdata: '=',
controllername:'#'
},
template: '<div ng-include="getContentUrl()"></div>',
controller: '#',
name: 'controllername'
};
return directive;
function link(scope, element, attrs) {
scope.getContentUrl = function () {
return '/app/views/TasksViews/' + scope.type + '.html';
}
scope.getControllerName = function ()
{
console.warn("Controller Name is " + scope.type);
return scope.type;
}
}
}
})();
Here how I'm trying to use the directive
<div ng-controller="WorkflowHistoryController as vm">
<h2>Workflow History</h2>
<h3>{{Id}}</h3>
<div ng-repeat="workflowStep in CompletedWorkflowSteps">
<bpm-completed-task controllername="workflowStep.WorkflowTaskType.DataMessageViewViewName" taskdata="workflowStep.WorkflowTaskOutcome.TaskOutcome" type="workflowStep.WorkflowTaskType.DataMessageViewViewName">
</bpm-completed-task>
</div>
</div>
The problem now is when the directive gets the controller name it get it as literal string not as a parameter.
Is it doable ?
if it's not doable, What is the best solution to create dynamic views with its controllers and display them dynamically inside ng-repeat?
Thanks,
Update 20 Jan I just updated my code in case if some one interested in it. All the Credit goes to #Meligy.
The First Directive:
(function () {
'use strict';
angular
.module('BPM.Directives')
.directive('bpmCompletedTask', bpmCompletedTask);
bpmCompletedTask.$inject = ['$compile', '$parse'];
function bpmCompletedTask ($compile, $parse) {
var directive = {
link: function (scope, elem, attrs) {
console.warn('in the first directive - before if');
if (!elem.attr('bpm-completed-task-inner'))
{
console.warn('in the first directive');
var name = $parse(elem.attr('controllername'))(scope);
console.warn('Controller Name : ' + name);
elem = elem.removeAttr('bpm-completed-task');
elem.attr('controllernameinner', name);
elem.attr('bpm-completed-task-inner', '');
$compile(elem)(scope);
}
},
restrict: 'A',
};
return directive;
}
})();
The Second Directive
angular
.module('BPM.Directives')
.directive('bpmCompletedTaskInner',['$compile', '$parse',
function ($window, $compile, $parse) {
console.warn('in the second directive');
return {
link: function (scope, elem, attrs) {
console.warn('in the second directive');
scope.getContentUrl = function () {
return '/app/views/TasksViews/' + scope.type + '.html';
}
},
restrict: 'A',
scope: {
type: '=',
taskdata: '=',
controllernameinner: '#'
},
template: '<div ng-include="getContentUrl()"></div>',
controller: '#',
name: 'controllernameinner'
};
}]);
The Html
<div ng-repeat="workflowStep in CompletedWorkflowSteps">
<div bpm-completed-task controllername="workflowStep.WorkflowTaskType.DataMessageViewViewName" taskdata="workflowStep.WorkflowTaskOutcome.TaskOutcome"
type="workflowStep.WorkflowTaskType.DataMessageViewViewName">
</div>
</div>
Update:
I got it working, but it's really ugly. Check:
http://jsfiddle.net/p6Hb4/13/
Your example has a lot of moving pieces, so this one is simple, but does what you want.
Basically you need a wrapper directive that takes the JS object and converts into a string property, then you can use هى your directive for everything else (template, scope, etc).
.
Update 2:
Code Inline:
var app = angular.module('myApp', []).
directive('communicatorInner', ["$parse", "$compile",
function($parse, $compile) {
return {
restrict: 'A',
template: "<input type='text' ng-model='message'/><input type='button' value='Send Message' ng-click='sendMsg()'><br/>",
scope: {
message: '='
},
controller: '#'
};
}
]).
directive('communicator', ['$compile', '$parse',
function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem) {
if (!elem.attr('communicator-inner')) {
var name = $parse(elem.attr('controller-name'))(scope);
elem = elem.removeAttr('controller-name')
elem.attr('communicator-inner', name);
$compile(elem)(scope);
}
}
};
}
]).
controller("PhoneCtrl", function($scope) {
$scope.sendMsg = function() {
alert($scope.message + " : sending message via Phone Ctrl");
}
}).
controller("LandlineCtrl", function($scope) {
$scope.sendMsg = function() {
alert($scope.message + " : sending message via Land Line Ctrl ");
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
<div ng-app="myApp">
<div ng-init="test = {p: 'PhoneCtrl', l: 'LandlineCtrl' }">
<communicator controller-name="test.p" message="'test1'"></communicator>
<communicator controller-name="test.l"></communicator>
</div>
</div>
.
Original (irrelevant now but can help other related issues)
Yes, it should work.
A test with Angular 1.3:
http://jsfiddle.net/p6Hb4/9/
Things to check:
Is the controller defined and added to the module? It will not work
If the controller is just a global function it won't work. It has to be added via the <myModule>.controller("<controllerName>", <functiion>) API
Does ng-controller work? Just adding it to the template
Similarly, does using ng-controller directly outside of the directive work?

Categories