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
};
}]);
Related
(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.
I am trying to add $watch in my directive. The directive is for wizard creation , when I read an existing array and use ng-repeat in directive it works as expected but If I update my array the newly added items in array doesn't add in directive to create wizard.
Please refer This plunker to understand the issue. At this plunker script.js has array data which I will be updating on a button click and read them back in ng-repeat under wizard directive on html page.
the directive is
directive('wizard', function($timeout, $compile) {
var opts = {
headerTag: "h3",
bodyTag: "section",
transitionEffect: "slideLeft",
autoFocus: true,
enableAllSteps: true,
enableKeyNavigation: false,
enableCancelButton: true,
enableFinishButton: true,
showFinishButtonAlways: false
};
return {
restrict: 'E',
replace: true,
template: '<div class="mywizard"></div>',
compile: function (tEl) {
return function(scope, element, attrs, ngModel) {
var stepsEl;
element.wrapInner('<div class="steps-wrapper">');
element.children('.steps-wrapper').append(tEl.context.innerHTML);
var content = $compile(element.contents())(scope);
$timeout(function () {
element.children('.steps-wrapper').steps(opts);
})
}
}
};
});
Please share idea where should I apply the watch.
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)"
I define a ui-grid to display data, and I define a cell template to set the column style. At the same time I also create a directive, here I just add it to the cell template. But the link function execution times is less than expectation.
Here's the whole thing on plunker: LINK
var app = angular.module("app", ['ui.grid']);
app.controller("dataCtrl", function ($scope, $element, $attrs) {
var vm = this;
vm.gridOptions = {
data: "ctrl.dataList",
columnDefs: [
{
name: "ID",
displayName: "User ID",
width: 200
},
{
name: "Name", displayName: "User Name",
cellTemplate: "<div class=\"ui-grid-cell-contents\" pop-tip><span style=\"margin-left:5px\">{{row.entity[\"Name\"]}}</span></div>"
}
],
enableRowSelection: true,
enableRowHeaderSelection: false,
multiSelect: false,
noUnselect: true,
};
vm.dataList = [];
vm.loadData = function () {
for (var i = 1; i <= 100; i++) {
var user = {};
user.ID = i;
user.Name = 'user ' + i;
vm.dataList.push(user);
}
}
vm.loadData();
});
app.directive("popTip", function ($compile) {
return {
restrict: 'A',
scope: false,
link: function ($scope, $element, $attrs) {
console.log($scope.row.entity.Name);
}
};
})
You can get the browser log to view the time of link execution.
The result is that when the data amount is large that appears an vertical scroll, when we drag scroll bar the custom directive will not execute link function anymore.
It's quite likely that there is some optimization built into ui-grid, whereby they reuse already-linked row elements, rather than link new ones.
You could inspect that (and, it should get you what you need) by $watch-ing the changes in the scope:
link: function ($scope, $element, $attrs) {
//console.log($scope.row.entity.Name);
$scope.$watch("row.entity.Name", function(v){
console.log(v);
});
}
This will display all the rows when scrolling.
Demo
I have a custom directive that uploads a file to amazon and contains a callback(onComplete).
When the callback is complete, I would like to attach a value to the $scope of the controller in which the directive is created. In this case, the scope of Invite.
Both Invite and fineUploader extend the same angular module.
HTML(simplified):
<div ng-controller="Invite" class="apply">
<div fine-uploader ng-switch-when="file" upload-extensions="jpg,jpeg,png,gif"></div>
</div>
Directive:
directive('fineUploader', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function($scope, element, attributes, ngModel) {
$scope.uploader = new qq.s3.FineUploader({
debug: true,
element: element[0],
request: {
endpoint: 'ballentines-bar-project.s3.amazonaws.com',
accessKey: 'AKIAIPT6J4T6XZXV3VWA'
},callbacks: {
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success === true) {
console.log(this.getKey(id));
console.log($scope);
$scope.test = this.getKey(id);
}
}
},
signature: {
endpoint: '/s3/'
},
iframeSupport: {
localBlankPagePath: '/success.html'
},
retry: {
enableAuto: true // defaults to false
},
deleteFile: {
enabled: false
},
text: {
uploadButton: '<p>Upload File</p>'
},
template:
'<div class="qq-uploader">' +
'<div class="qq-upload-button btn btn-info">{uploadButtonText}</div>' +
'<ul class="qq-upload-list" ><h2>Your files</h2></ul>' +
'</div>',
});
}
};
}).
Controller
controller('Invite', function(
$scope,
$localStorage,
$http
){
$scope.$storage = $localStorage.$default({
"formkey": "1MRSAWTRl5-PnVEoy3tD63BL3q_v2mnAhtqa9bdZk-zg",
"draftResponse": "[]",
"pageHistory": "0",
});
$scope.liking = liking;
$scope.post = function(){
$http.post('/signup.php', $scope.$storage).
success(function(data, status, headers, config){
console.log(data);
});
};
FB.Event.subscribe('edge.create',
function(href, widget) {
liking = true;
}
);
})
You either need to pass the items from the parent scope to the directive (through isolated scope). Or, do as #MaximShoustin says and remove the isolated scope from your directive.
So, option 1:
scope: { directiveProperty: '=nameOfAttributeThatContainsParentProperty' },
Or, option 2:
Remove the isolated scope declaration scope: {}, from the directive. This will allow the directive to extend the scope of it's containing scope.
I would try at least two options:
[1]
change scope: {}, in directive to:
`scope: { test: '#'},`
This makes the test method visible in the private scope.
[2]
The second option try removing the isolate scope a.e: scope: {},