AngularJS ES6 ui-grid directive scope, ng-click dont work - angularjs

I have a chain directive with angularJS and ES6 and want to use ui-grid.
The grid is shown with the correct columns and data, thats fine.
But ng-click donĀ“t work in the cellTemplate. Nothing happens.
Also i try to check the grid object with console.log(grid) and grid is undefined.
Who can i use the cellTemplate to call the openDetail method?
export default function ChainsDirective() {
class ChainsDirective {
/*#ngInject*/
constructor(chainsService, $state) {
this.chainsServiceLoadChains = chainsService.loadChains.bind(chainsService);
this.gridOptions = {
enableColumnMenus: false,
columnDefs: [
{
name: 'id',
visible: false
},
{
name: 'name',
displayName: 'Kette',
cellTemplate: '<div class="ui-grid-cell-contents"><a onclick="console.log(grid)" ng-click="grid.appScope.openDetail(row.entity.id)">{{row.entity.name}}</a></div>'
}
]
};
this.$stateGo = $state.go.bind($state);
this.fetch(); // best practice initiales laden in einer Funktion zu kapseln
}
/**
* #param int chainId
*/
openDetail(chainId) {
this.$stateGo('chainDetail', {chainId})
}
fetch() {
return this.chainsServiceLoadChains().then(data => {
this.gridOptions.data = data;
})
}
}
return {
restrict: 'E',
template: '<div ui-grid="chains.gridOptions" external-scopes="$scope" class="grid"></div>',
scope: {},
bindToController: {},
controller: ChainsDirective,
controllerAs: 'chains'
}
}

I've never used ui-grid directive. But it seems that you need to access your controller by its name, since you have declared it with the "controller as" syntax.
So in your ng-click you need to reference the controller on the scope with its name chains:
ng-click="grid.appScope.chains.openDetail(row.entity.id)"

Related

2-way binding inside directive loop

I am trying to write a simple form builder for my clients. The idea being for them to create a simple form that they can use later on different occasions.
For that I am at this point creating a directive to parse the json form back to html trough a directive.
angular
.module('myApp')
.directive('formbuilder', ['$timeout', '$compile', '$parse', function($timeout, $compile, $parse) {
return {
restrict:'AE',
require: 'ngModel',
scope: {
form: '=ngModel'
},
link: function(scope, element, attrs) {
$timeout(function() {
var bones = scope.form.structure;
scope.formId = scope.form.title.replace(/\W+/g, " ").replace(/\s/g,'').toLowerCase();
var html = '<form id="{{formId}}" class="row">';
angular.forEach(bones, function(bone, key) {
if(bone.type == 'text' || bone.type == 'checkbox') {
scope[key] = $parse('form.data.'+key)(scope);
html += '<input-field class="col s12"><input type="'+bone.type+'" id="'+bone.type+key+'" ng-model="form.data['+key+']" /> <label for="'+bone.type+key+'">'+bone.label+'</label></input-field> ';
}
})
html += '<p>{{form.data.input1}}</p>';
html += '</form>';
element.append($compile(html)(scope));
})
}
};
}]);
Problem is: I am looping through the items to find parse them back into html. That is clearly not working as expected. I can $parse it but then the 2-way binding is lost...
Any thoughts ?
The json structure is:
$scope.form = {
title: 'My first form',
structure: {
input1: {
type: 'text',
label: 'Input label'
},
input2: {
type: 'checkbox',
label: 'This is a checkbox'
},
input3: {
type: 'checkbox',
label: 'This is a CHECKED checkbox'
}
},
data: {
input1: 'Yannick',
input2: false,
input3: true
}
}
I would avoid using the ng-model attribute which instantiates the ngModelController. Instead use one-time binding:
<formbuilder form="::form"></formbuilder>
In the directive, use isolate scope with one-way (<) binding:
.directive('formbuilder', ['$timeout', '$compile', '$parse', function($timeout, $compile, $parse) {
return {
restrict:'AE',
/*
require: 'ngModel',
scope: {
form: '=ngModel'
},
*/
scope: { form: "<" },
This will bind the form object reference to the isolate scope. Changes to the contents by the directive inputs will be shared with the parent scope object. There is no need to do two-way binding of the object reference.
Also for obvious reasons, don't use bracket notation for the property accessor, instead use dot notation:
//html += '<input ng-model="form.data['+key+']"'
//USE dot notation
html += '<input ng-model="form.data.'+key+'"'
The DEMO on PLNKR.

how to pass callback funtion to Angular JS dirctive

I am trying to create a footer directive that I can pass an array of button objects to.
here is my array...
$scope.buttons = [
{
btnLabel: "Close",
btnClass: "btn-p2 pull-left",
show: true,
disabled: false,
callback: function(){console.log('close...');},
role: ''
},
{
btnLabel: "Save",
btnClass: "btn-p1 pull-right",
show: true,
disabled: true,
callback: saveTest,
role: ''
}
];
I have most of it working the way I want but, having issue with passing the callback functions. I am seeing the buttons but when I click the callback functions are not getting called. Any ideas on how to pass the callback functions?
Thanks and regards,
dj
Because saveTest is not defined in your scope, directive doesn't have access to it. So either you have to defined them on the scope and use them there, or you can pass them as directives attributes in its own scope:
First way:
$scope.saveTest = function () {
}
And then you can use the parent scope in your directive:
app.directive('someDirective', function() {
return {
restrict: 'E'
templateUrl: "..."
}
})
By not defining the scope in directive, you are saying I want to use parent scope here.
Second way:
app.directive('someDirective', function() {
return {
restrict: 'EA',
scope: {
saveTest: '&',
close: '&'
},
templateUrl: "..."
}
})
Now that we have defined them in the directive we have to pass them from html like this:
<div some-directive save-test="saveTest" close="close"></div>

angular with kendo grid in templateUrl

(sorry if this is duplicate, but what i've found does not answer my question:
Databinding is not working with kendo grid in angular JS directives
)
I am trying to create a directive with a templateUrl containing a kendo grid, and use this directive in a controller.
I can manage to bind some of the directive's attributes (e.g. my-grid-options, which is initialized in the controller) but not my-grid-id.
What am I missing? (full plunk here)
the directive:
Directives.myGrid = function() {
return {
scope: {
myGridId: "=",
myGridOptions: "="
},
restrict: "E",
transclude: true,
templateUrl: 'myGrid.html',
link: function(scope) {
//... some code using the myGridId
scope.myGridId.....
}
}
}
myGrid.html:
<div kendo-grid="myGridId" k-options="myGridOptions" id="myGridId">
</div>
how I want to use it:
<body ng-app='app' ng-controller='myCtrl'>
<my-grid my-grid-id="mainGrid" my-grid-options="mainGridOptions"></my-grid>
</body>
controller:
angular.module('app').controller('myCtrl',function($scope){
$scope.ds = [
{Category:"Toys", Name: "Doll", Code: "p1", Special: false},
....
{Category:"Stationary", Name: "Crayons", Code: "p4", Special: false}
];
$scope.mainGridOptions = {
columns: [
{
field: "Category",
title: "Category"
},
{
field: "Name",
title: "Name"
},
{
field: "Code",
title: "Code"
},
{
field: "Special",
title: "Special Offer"
},
],
dataSource: $scope.ds,
sortable: true,
selectable: true,
resizable: true,
pageable: true,
reorderable: true,
columnReorder: function (e) {
//do something trivial... for example sake, but a more complex event is used
$.each($scope.mainGrid.wrapper.find('tbody tr'), function () {
var model = $scope.mainGrid.dataItem(this);
if (model.Special === true) {
$(this).addClass('special-row');
}
});
}
};
});
Thanks for any advice.
Ok, so i got to see your plunk. I did a few changes and here is the result.
You still got some problems in there. Check my changes in your directive:
angular.module('app').directive('myGrid', function(){
return {
scope: {
gridId: "=",
gridOptions: "="
},
restrict: "E",
replace: true,
templateUrl: 'myGrid.html',
link: function(scope) {
console.log(gridId);
console.log(scope.gridId);
// var doMore = function() {
// $.each(scope.gridId.wrapper.find('tbody tr'), function () {
// var model = $scope.gridId.dataItem(this);
// if (model.Category === "Stationary") {
// $(this).addClass('stationary-row');
// }
// });
// };
// var extendReorder = function(baseReorder) {
// doMore();
// if (baseReorder)
// baseReorder();
// };
scope.gridOptions.columnReorder = extendReorder(scope.gridOptions.columnReorder);
}
};
});
I removed the tranclude:"true", as it caused a directive collision. Then there were errors about you gridId being "undefined". I don't know what its purpose is, so i just gave it a value in the controller (check the plunk). If it is not completely what you want yet, let me know, i look further into it.
Thanks to #ocket-san for taking the time to look at this.
Eventually I got what I wanted: to extend the kendo-grid inside a directive without affecting any controller implementations which use the kendo-grid.
I managed to do this by creatting a directive of Attribute type which extends the current definition (usage) of the kendo-grid, so wherever I wish to add my extensions I can simply add my directive as an attribute:
<div kendo-grid="mainGrid" k-options="mainGridOptions" id="mainGrid" my-grid >
Instead of:
<div kendo-grid="mainGrid" k-options="mainGridOptions" id="mainGrid">
(fyi: I wanted to set pagination buttons to both top and bottom for all the kendo-grids in my application, without having to duplicate the extension all over the place)
See plunker here.

Using Angular template for Kendo UI Grid detail template

I am using the Kendo UI package for Angular JS. I would like to use an Angular template for the row detail, very similar to what is done here:
Kendo Grid Detail Template
Essentially I would like to fetch some data when details are expanded, and pass an angular model to the template. Is this possible?
What I have done (so far) with this same need is use the changed event on the Grid to populate a 'SelectedRow' object on the $scope in my controller. For the DetailTemplate, I have a div that contains a directive that loads the template either from the $templateCache or using $http and compiles it and links it to the $scope. One of the problems with templates is compiling and linking them to the $scope and the timing of when that takes place. (My problem was even worse as I needed a different detail template for each row)
$scope.vm.options.productGridOptions = {
dataSource: new kendo.data.DataSource({
data: $scope.vm.solution.Products,
pageSize: 10
}),
change: $scope.vm.events.productSelected,
columns: $scope.vm.columns.productColumns,
detailTemplate: '<div data-template-detail type="#= EntityTemplateSK #"></div>',
filterable: false,
groupable: false,
pageable: true,
reorderable: true,
resizable: true,
selectable: 'single',
sortable: true
};
myApp.directive('templateDetail', ['$compile', '$http', '$templateCache',
function ($compile, $http, $templateCache) {
var detailTemplateNumbers = ['21', '22', '23', '26', '45', '69'];
var getTemplate = function (templateNumber) {
var baseUrl = '/App/Product/Views/',
templateName = 'productdetail.html',
templateUrl = baseUrl + templateName;
if (detailTemplateNumbers.filter(function (element) { return element === templateNumber; })[0]) {
templateName = 'productTemplate' + templateNumber + '.html';
templateUrl = baseUrl + templateName;
}
return $http.get(templateUrl, { cache: $templateCache });
};
var linker = function ($scope, element, attrs) {
var loader = getTemplate(attrs.type.toString());
if (loader) {
loader.success(function (html) {
element.html(html);
}).then(function () {
element.replaceWith($compile(element.html())($scope.$parent));
});
}
};
return {
restrict: 'A',
scope: {
type: '='
},
link: linker
};
}]);

Class directive not working when using ng-class to load the directive

I am trying to load a 'class' directive using ng-class. but my directive is never loaded when i do that. The directive is a multipurpose directive, and I don't want to create an isolated scope on this. it will be loaded only when required, based on ng-class conditions hence not using attribute or element directive. has anyone tried doing this and succeeded?
this directive is called using <div ng-class="someClass {{tooltip: enabled}}"></div>
here enabled is a scope variable.
app.directive('tooltip', ['$timeout', '$location', '$rootScope', function (timer, $location, $rootScope) {
return {
restrict: 'C',
transclude: true,
link: function (scope, element) {
var printContent = function () {
/* uses the content of .tooltip-content if it is a complex html tooltip,
otherwise
you can use the title attribute for plaintext tooltips
*/
var tooltipContent = $(element).find('.tooltip-content').html();
if (!tooltipContent) {
tooltipContent = $(element).attr('title');
}
$(element).tooltip({
content: tooltipContent,
items: "img, a, span, button, div",
tooltipClass: "tooltip",
position: { my: "left+30 top", at: "right top", collision: "flipfit" },
show: { effect: "fadeIn", duration: "fast" },
hide: { effect: "fadeOut", duration: "fast" },
open: function (event, ui) { $rootScope.tooltipElement = event.target; }
});
};
timer(printContent, 0);
}
};
}]);
Interesting issue. It seems that you don't want to use the ng-class directive since that doesn't recompile the content after adding the class. You'll likely want to create your own dynamic-class directive that actually recompiles when the value is true:
app.directive('dynamicClass', function($compile) {
return {
scope: {
dynamicClassWhen: '=',
dynamicClass: '='
},
link: function(scope, elt, attrs) {
scope.$watch('dynamicClassWhen', function(val) {
if (val) {
console.log(val);
elt.addClass(scope.dynamicClass);
$compile(elt)(scope);
}
});
}
};
});
You may need to modify this for the ability to remove the class and depending on if the $compile is sufficient for you or if you need to further manipulate the html, but this seems to be the right track for you. I made a fiddle with this in action.
Hope this helps!

Resources