Is there a way for DTColumnBuilder.newColumn.renderWidth to include custom directives? Here is a draft code of what I want to achieve.
DTColumnBuilder.newColumn('reportStructureName').withTitle('Structure Name')
.renderWith((data, type, full) => {
return "<my-directive></my-directive>";
}),
You can $compile the cell content in the createdCell callback. Here is a very simple example, with a directive that does nothing but coloring the text red. Sorry for not using arrow functions :)
$scope.data = [
{ reportStructureName : "structurename1" },
{ reportStructureName : "structurename2" },
{ reportStructureName : "structurename3" },
{ reportStructureName : "structurename4" }
]
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('data', $scope.data)
.withPaginationType('full_numbers');
$scope.dtColumns = [
DTColumnBuilder.newColumn('reportStructureName')
.withTitle('Structure Name')
.renderWith(function(data, type, full) {
return "<my-directive>"+data+"</my-directive>";
})
.withOption('createdCell', function(td, cellData, rowData, row, col) {
$compile( td )( $scope ); //<--- here
})
]
Directive :
.directive('myDirective', function() {
return {
restrict: 'AE',
link: function (scope, element, attr, ctrl) {
angular.element(element).css('color', 'red')
}
}
})
demo -> http://plnkr.co/edit/aok6SyWZlLaQv8UsEVIf?p=preview
Related
I using elastic directive for resizing textarea from this answer.
But i using ng-show for textarea, and on click height of textarea is 0.
So i need to use $watch somehow to trigger directive on click, but don't know how.
Html:
<textarea ng-show="showOnClick" elastic ng-model="someProperty"></textarea>
<a ng-click="showOnClick = true"> Show text area </a>
Directive:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
link: function($scope, element) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);
Here is JSFIDDLE
as requested, the solution is to $watch the ngShow attr and run some sort of init function when the value is true.
user produced jsfiddle
example code:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
scope: {
ngShow: "="
},
link: function($scope, element, attr) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
if (attr.hasOwnProperty("ngShow")) {
function ngShow() {
if ($scope.ngShow === true) {
$timeout(resize, 0);
}
}
$scope.$watch("ngShow", ngShow);
setTimeout(ngShow, 0);
}
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);
I have a 'regionMap' directive that includes methods for rendering and destroying the map. The map is rendered inside of a modal and upon clicking the modal close button the 'regionMap' destroy method is called, which should remove the element and scope from the page. However, when returning to the modal page, that includes the 'region-map' element, the previous 'region-map' element is not removed, resulting in multiple maps being displayed. What is the correct way to remove the regionMap directive from the page when the modal is closed?
// directive
(function(){
'use strict';
angular.module('homeModule')
.directive('regionMap', regionMap);
function regionMap() {
var directive = {
restrict: 'E',
template: '',
replace: true,
link: link,
scope: {
regionItem: '=',
accessor: '='
}
}
return directive;
function link(scope, el, attrs, controller) {
if (scope.accessor) {
scope.accessor.renderMap = function(selectedRegion) {
var paper = Raphael(el[0], 665, 245);
paper.setViewBox(0, 0, 1100, 350, false);
paper.setStart();
for (var country in worldmap.shapes) {
paper.path(worldmap.shapes[country]).attr({
"font-size": 12,
"font-weight": "bold",
title: worldmap.names[country],
stroke: "none",
fill: '#EBE9E9',
"stroke-opacity": 1
}).data({'regionId': country});
}
paper.forEach(function(el) {
if (el.data('regionId') != selectedRegion.name) {
el.stop().attr({fill: '#ebe9e9'});
} else {
el.stop().attr({fill: '#06767e'});
}
});
}
scope.accessor.destroyMap = function() {
scope.$destroy();
el.remove();
}
}
}
}
})();
// controller template:
<region-map accessor="modalvm.accessor" region-item="modalvm.sregion"></region-map>
// controller:
vm.accessor = {};
...
function showMap() {
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap) {
vm.accessor.renderMap(vm.sregion);
}
});
function closeMap() {
if (vm.accessor.destroyMap) {
vm.accessor.destroyMap();
}
$modalInstance.dismiss('cancel');
}
The issue is related to loading a template with a directive inside of it. Fixed it by adding a var to check if the map has previously been rendered:
vm.accessor.mapRendered = false;
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap && !vm.accessor.mapRendered) {
vm.accessor.renderMap(vm.selectedRegions);
vm.accessor.mapRendered = true;
}
});
Im' working on the Malhar Angular Dashboard, based on this github project https://github.com/DataTorrent/malhar-angular-dashboard.
As per the documentation in the link post just above, under the 'dataModelType' heading 1/2 way down:
`The best way to provide data to a widget is to specify a dataModelType in the Widget Definition Object (above). This function is used as a constructor whenever a new widget is instantiated on the page.`
And when setting up the Widget Definition Objects, there are various options to choose from :
templateUrl - URL of template to use for widget content
template - String template (ignored if templateUrl is present)
directive - HTML-injectable directive name (eg. "ng-show")
So when I add my own widget definition column chart, I attempt to use the 'template' option; however it does NOT inject the {{value}} scope variable I'm setting.
Using the original datamodel sample widget def, it works fine using the 'directive' option. If I mimic this method on my column chart definition then it works ! But it doesn't work using the template option.
Here's the 'widgetDefinitions' factory code :
(function () {
'use strict';
angular.module('rage')
.factory('widgetDefinitions', ['RandomDataModel','GadgetDataModel', widgetDefinitions])
function widgetDefinitions(RandomDataModel, GadgetDataModel) {
return [
{
name: 'datamodel',
directive: 'wt-scope-watch',
dataAttrName: 'value',
dataModelType: RandomDataModel // GOTTA FIGURE THIS OUT !! -BM:
},
{
name: 'column chart',
title: 'Column Chart',
template: '<div>Chart Gadget Here {{value}}</div>',
dataAttrName: 'value',
size: {width: '40%',height: '200px'},
dataModelType: ColumnChartDataModel
},
];
}
})();
and here are the factories:
'use strict';
angular.module('rage')
.factory('TreeGridDataModel', function (WidgetDataModel, gadgetInitService) {
function TreeGridDataModel() {
}
TreeGridDataModel.prototype = Object.create(WidgetDataModel.prototype);
TreeGridDataModel.prototype.constructor = WidgetDataModel;
angular.extend(TreeGridDataModel.prototype, {
init: function () {
var dataModelOptions = this.dataModelOptions;
this.limit = (dataModelOptions && dataModelOptions.limit) ? dataModelOptions.limit : 100;
this.treeGridActive = true;
//this.treeGridOptions = {};
this.updateScope('THIS IS A TreeGridDataModel...'); // see WidgetDataModel factory
},
updateLimit: function (limit) {
this.dataModelOptions = this.dataModelOptions ? this.dataModelOptions : {};
this.dataModelOptions.limit = limit;
this.limit = limit;
},
destroy: function () {
WidgetDataModel.prototype.destroy.call(this);
}
});
return TreeGridDataModel;
});
'use strict';
angular.module('rage')
.factory('ColumnChartDataModel', function (WidgetDataModel) {
function ColumnChartDataModel() {
}
ColumnChartDataModel.prototype = Object.create(WidgetDataModel.prototype);
ColumnChartDataModel.prototype.constructor = WidgetDataModel;
angular.extend(ColumnChartDataModel.prototype, {
init: function () {
var dataModelOptions = this.dataModelOptions;
this.limit = (dataModelOptions && dataModelOptions.limit) ? dataModelOptions.limit : 100;
this.treeGridActive = true;
var value = 'THIS IS A ColChartDataModel...';
//$scope.value = value;
this.updateScope(value); // see WidgetDataModel factory
},
updateLimit: function (limit) {
this.dataModelOptions = this.dataModelOptions ? this.dataModelOptions : {};
this.dataModelOptions.limit = limit;
this.limit = limit;
},
destroy: function () {
WidgetDataModel.prototype.destroy.call(this);
}
});
return ColumnChartDataModel;
});
and finally the directives:
'use strict';
angular.module('rage')
.directive('wtTime', function ($interval) {
return {
restrict: 'A',
scope: true,
replace: true,
template: '<div>Time<div class="alert alert-success">{{time}}</div></div>',
link: function (scope) {
function update() {
scope.time = new Date().toLocaleTimeString();
}
update();
var promise = $interval(update, 500);
scope.$on('$destroy', function () {
$interval.cancel(promise);
});
}
};
})
.directive('wtScopeWatch', function () {
return {
restrict: 'A',
replace: true,
template: '<div>Value<div class="alert alert-info">{{value}}</div></div>',
scope: {
value: '=value'
}
};
})
.directive('wtFluid', function () {
return {
restrict: 'A',
replace: true,
templateUrl: 'app/views/template2/fluid.html',
scope: true,
controller: function ($scope) {
$scope.$on('widgetResized', function (event, size) {
$scope.width = size.width || $scope.width;
$scope.height = size.height || $scope.height;
});
}
};
});
I'd like to know why ONLY the directive option will update the wigdet's data and not the template option.
thank you,
Bob
I believe I see the problem. The dataAttrName setting and updateScope method are actually doing something other than what you're expecting.
Look at the makeTemplateString function here. This is what ultimately builds your widget's template. You should notice that if you supply a template, the dataAttrName does not even get used.
Next, take a look at what updateScope does, and keep in mind that you can override this function in your own data model to do what you really want, a la:
angular.extend(TreeGridDataModel.prototype, {
init: function() {...},
destroy: function() {...},
updateScope: function(data) {
// I don't see this "main" object defined anywhere, I'm just going
// off your treegrid.html template, which has jqx-settings="main.treeGridOptions"
this.widgetScope.main = { treeGridOptions: data };
// Doing it without main, you could just do:
// this.widgetScope.treeGridOptions = data;
// And then update your treegrid.html file to be:
// <div id="treeGrid" jqx-tree-grid jqx-settings="treeGridOptions"></div>
}
});
I have a pair of elements that I want to hide or show depending on if photo data is available. On the initial page load this works fine, however, if a photo is added via the capturePhoto method in the directive I have to reload the page to have the elements show/hide as they should.
.html
<button ng-hide="measurement.photo != ''" class="btn btn-default" ng-click="capturePhoto(measurement)">Photo</button>
<img ng-show="measurement.photo != ''" id="{{measurement.__id}}" src="{{measurement.photo}}" width="100" height="100"/>
.js
.directive( 'sidingMeasurementGroup', function( $spawn, $measurementGroups, $compile, $projects, $filter, $photo )
{
return {
scope:
{
group: "=",
measurementTarget: "="
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
templateUrl: 'partials/directives/siding-measurement-group.html',
transclude: true,
replace:true,
link: function( $scope, iElm, iAttrs, controller )
{
$scope.capturePhoto = function(measurement)
{
function fail( e )
{
$error.
throw ( 'problem taking photo', e );
}
function success( imageURI )
{
cordova.exec(
function callback(data) {
measurement.photo = data.filePath;
var image = document.getElementById(measurement.__id);
image.src = null;
image.src = measurement.photo;
},
function errorHandler(err) {
alert('Error');
},
'CopyFiles',
'copyFileFromTmp',
[ imageURI ]
);
}
$photo.takePhoto( success, fail );
}
}
};
} )
If I log the value of measurement.photo after setting it, the correct value is shown, but on the HTML side the elements don't change their visibility. How do I make them react to the change that took place in my directive?
have you tried scope.$apply();
Edit to add:
This worked perfectly, this is my resulting javascript code using scope.$apply()
.directive( 'sidingMeasurementGroup', function( $spawn, $measurementGroups, $compile, $projects, $filter, $photo )
{
return {
scope:
{
group: "=",
measurementTarget: "="
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
templateUrl: 'partials/directives/siding-measurement-group.html',
transclude: true,
replace:true,
link: function( $scope, iElm, iAttrs, controller )
{
$scope.capturePhoto = function(measurement)
{
function fail( e )
{
$error.
throw ( 'problem taking photo', e );
}
function success( imageURI )
{
cordova.exec(
function callback(data) {
$scope.$apply(function () {
$scope.photoTaken = true;
measurement.photo = data.filePath;
var image = document.getElementById(measurement.__id);
image.src = null;
image.src = measurement.photo;
});
},
function errorHandler(err) {
alert('Error');
},
'CopyFiles',
'copyFileFromTmp',
[ imageURI ]
);
}
$photo.takePhoto( success, fail );
}
I am attempting to write a directive with it's own controller.
myApp.directive('imageUploadifive', function (createGal)
{
return {
restrict: 'A',
controller: function($scope)
{
//create gallery
$scope.created = createGal.createGal();
$scope.created.then(function(createGal){
$scope.gallery.id = createGal.created_id;
console.log($scope.gallery.id);//returning after the link function
});
$scope.gallery.galleryName = "New Image Gallery";
},
link: function($scope, element, attrs)
{
var id = $scope.gallery.id;
console.log(id);
$(element).uploadifive({
'uploadScript' : '/beta/images/upload',
'buttonClass' : 'uploadifive-button btn btn-primary',
'queueID' : 'imageGallery_queue',
'buttonText' : 'Select Files',
'fileSizeLimit' : 500,
'formData' : {
'galleryID' : id
},
'onError': function(errorType)
{
alert('There was a problem');
},
'onUpload': function()
{
}
});
}
};
});
This directive is called with a modal and the //create gallery is generating an id for the uploader js. What I don't understand is the link: function is running and returning undefined before the controller. Any guidance on this would be appreciated.
Thanks
It's not the linking function is called before the controller, it's because the $scope.created.then() is asynchronous, the callback function to set the id is called after the linking function.
To fix it, you need to call $scope.created.then() in the linking function instead:
link: function($scope, element, attrs) {
$scope.created.then(function(createGal) {
$scope.gallery.id = createGal.created_id;
$(element).uploadifive({
'uploadScript' : '/beta/images/upload',
'buttonClass' : 'uploadifive-button btn btn-primary',
'queueID' : 'imageGallery_queue',
'buttonText' : 'Select Files',
'fileSizeLimit' : 500,
'formData' : {
'galleryID' : $scope.gallery.id
},
'onError': function(errorType)
{
alert('There was a problem');
},
'onUpload': function()
{
}
});
});
}