I am trying to create a kendo grid (angularjs) and attached a personalized editor <div my-directive-editor></div> via grid options editable.template. On my directive editor (angularjs directive), i specify the structure of HTML from remote file and link it via templateUrl. Upon running the application, everything works great when i first click the Add New Entry but when i cancel the popup dialog and click again the Add New Entry an error will show $digest already in progress in angular format.
I tried instead using templateUrl I used template and formatting the whole HTML structure as string and passed it there and it goes well without the error but as i can see, it is hard for the next developer to manage the very long HTML string so it would be great if i can separate it to remote file and just link it to templateUrl. I prepared a dojo to play with CLICK HERE the content of TestTemplate.html is the HTML string from template.
This is my directive
app.directive('grdEditor',
[
function () {
return {
restrict: 'A',
replace: true,
scope: {
dataItem: '=ngModel'
},
//template: '<div><table><tr><td>Name</td><td><input ng-model="dataItem.Name" class="k-input k-textbox" /></td></tr><tr><td>Birthdate</td><td><input kendo-date-picker k-ng-model="dataItem.Birthdate" /></td></tr><tr><td>Gender</td><td><input kendo-combo-box k-ng-model="dataItem.Gender" k-options="optGender" /></td></tr></table></div>',
templateUrl: 'http://localhost/Angular/TestTemplate.html',
/*template: function(){
return '<div><table><tr><td>Name</td><td><input ng-model="dataItem.Name" class="k-input k-textbox" /></td></tr><tr><td>Birthdate</td><td><input kendo-date-picker k-ng-model="dataItem.Birthdate" /></td></tr><tr><td>Gender</td><td><input kendo-combo-box k-ng-model="dataItem.Gender" k-options="optGender" /></td></tr></table></div>';
},*/
controller: function ($scope, $attrs, $timeout) {
$scope.optGender = {
dataTextField: 'Text',
dataValueField: 'Value',
dataSource:
{
data: [
{
Text: 'Male',
Value: 1
},
{
Text: 'Female',
Value: 2
}]
}
};
}
};
}
]);
and this is my kendo grid options (partial)
$scope.optGrid = {
editable: {
mode: "popup",
window: {
minHeight: '320px',
minWidth: '365px',
},
template: '<div grd-editor ng-model="dataItem"></div>',
},
toolbar: ['create', 'excel'],
excel: {
allPages: true
},
.....................
Any help would be appreciated.
TIA
i think a there is problem with templateUrl. you don't need to give http://
you just need to give path from your base directory or directory of your index.html
Related
I am creating a wizard to add a new appointment in our application. The last page of the wizard contains a tabbed section with all potential conflicts based on several criteria. Each tab is one of the criteria and uses an Angular Grid to show the list of conflicts. Since each grid has the same columns, but contains different data, I would like to use a directive to wrap the Angular Grid and its grid options in the Template and then set the rowData in another attribute on my directive. I currently have the following for my directive:
'use strict';
app.directive('inApptConflict', ['angularGrid', function (angularGrid) {
return {
restrict: 'A',
transclude: true,
require: '?ngModel',
template: '<div class="ag-fresh conflictGrid" ag-grid="{{ conflictGridOptions }} ng-transclude"></div>',
controller: function ($scope) {
// function for displaying dates in grid
function datetimeCellRendererFunc(params) {...}
// column definitions
var conflictColumnDefs = [
{ colId: "Id", field: "Id", hide: true },
{ colId: "StartTime", field: "StartTime", headerName: "Start", width: 150, cellRenderer: datetimeCellRendererFunc } ...
];
// Grid options
$scope.conflictGridOptions = {
columnDefs: conflictColumnDefs,
rowData: null,
angularCompileRows: true,
enableColReseize: true
};
},
link: function ($scope, $elem, $attrs, ngModel) {
$scope.conflictGridOptions.rowData = ngModel;
$scope.conflictGridOptions.api.onNewRows();
}
};
}]);
My view has the following code:
<!-- Tab panes -->
<div role="tabpanel" class="tab-pane fade in active" id="conflicts1" data-ng-show="apptCtrl.conflicts1">
<div in-appt-conflict data-ng-model="apptCtrl.conflicts1"></div>
</div>
<div role="tabpanel" class="tab-pane fade" id="conflicts2" data-ng-show="apptCtrl.conflicts2">
<div in-appt-conflict data-ng-model="apptCtrl.conflicts2"></div>
</div>
Whenever I run this, I end up with the following error:
Error: [$injector:unpr] Unknown provider: angularGridProvider <- angularGrid <- inApptConflictDirective
I am not sure what else I need to do to get the directive to recognize ag-grid. I have tried using $compile, as well, but end up with the same error.
Is there something else that needs to be added to call a third party module from a directive? This did work when I used the grid three separate times with three separate grid options.
Thanks in advance for any help!
There is no need to inject 'angularGrid' in your directive (and there is no such injectable element).
All registered directives are available to all templates as soon as you register them in the angular module.
The only thing you need is to add 'agGrid' to the dependency of your angular module with something like
var module = angular.module("example", ["agGrid"]); then you case use ag-grid in your templates and directives.
See ag-grid documentation for more details.
So remove 'angularGrid' from line app.directive('inApptConflict', ['angularGrid', function (angularGrid) { and you should be good to go.
This is my Controller
$scope.usersList = {};
$scope.usersList = {
paginationPageSizes: [10,15, 20],
paginationPageSize: 10,
columnDefs: [
{ name: 'userId', cellTemplate: '<div class="ui-grid-cell-contents"><a ui-sref="appSetting.userSelected({userId: row.entity.userId})">{{ row.entity.userId }}</a></div>' },
{ name: 'firstName' },
{ name: 'lastName' },
{ name: 'emailId' },
{
name: 'action',
cellTemplate: '<div>' +
' <button ng-click="grid.appScope.sampledetails()">Delete</button>' +
'</div>',
enableSorting: false,
enableColumnMenu: false
}
]
};
and this is my .cshtml
<div id="grid1" ui-grid="gridOptions" class="grid"></div>
I want to write this in such a way that it should be used in other .cshmtls, but the columnDefs varies depending on table column name. How should I write in such a way that ths user should give the columnsDefs in directive along with pagination?
Your question is hard to understand, hopefully I got it right. You want to define default-settings for your grid but enable the user to input some special settings if needed?
Warp ui-grid in your own directive. Pass your wanted arguments into that directive and create default settings in your directive.
Your .cshtml. You pass your settings variable into that.
<my-grid options="usersList" />
Your Directive. Grab the settings there (see options: '=') and bind that to controller or scope.
angular.module('app').directive('myGrid', myGrid);
function myGrid() {
return {
templateUrl : 'grid.html',
controller : 'GridCtrl',
controllerAs : 'grid',
restrict: 'E',
scope: {
options : '=',
},
bindToController: true,
};
}
Your Controller. Now you can access your settings in that controller.
There you could combine the default settings with your inserted settings and pass that into the directive template.
angular.module('app').controller('GridCtrl', GridCtrl);
function GridCtrl() {
var grid = this;
console.log(grid.options); // containts your settings
grid.gridOptions = {
paginationPageSize: grid.options.paginationPageSize,
...,
columnDefs: grid.options.columnDefs
etc
}
}
And your grid.html, you pass the combined settings into the grid-API.
<div id="grid1" ui-grid="grid.gridOptions" class="grid"></div>
There are many more details to watch out for, but thats a possible setup.
e: I made a Plunkr for another similar question. For future reference.
I'm working on a open-source project for a AngularJS Data Table directive ( still WIP ). When you look at components like Angular Grid or UI Grid they all describe their columns and attributes in a object in the parent controller like:
$scope.gridOptions = {
enableSorting: true,
enableCellEditOnFocus: true,
columnDefs: [
{ name: 'field1', enableSorting: false, enableCellEdit: false },
{ name: 'field2' },
{ name: 'field3', visible: false }
]
};
which works fine, however, I don't think this is really the 'angular way'. It feels more like a jQuery widget. If you look at projects like Angular Material, they are much more expressive in the HTML template vs object driven.
For my implementation, I originally wanted to make it very expressive and expose each one of the inner directives I use, however, that ended up a mess to just create a basic table. So I did some research on other frameworks and found that react had a nice architecture where you just define the columns like:
React.render(
<Table
rowHeight={50}
rowGetter={rowGetter}
rowsCount={rows.length}
width={5000}
height={5000}
headerHeight={50}>
<Column
label="Col 1"
width={3000}
dataKey={0}
/>
<Column
label="Col 2"
width={2000}
dataKey={1}
/>
</Table>,
document.getElementById('example')
);
I feel in love with this approach, its simple and is expressive all at the same time. 90% of the time you only want to customize the column template anyways. So instead of this:
$scope.gridOptions = {
enableFiltering: true,
rowTemplate: rowTemplate(),
data: 'data',
columnDefs: [
{ name: 'name' },
{ name: 'gender' },
{ name: 'company' },
{ name: 'widgets' },
{
name: 'cumulativeWidgets',
field: 'widgets',
cellTemplate: '<div class="ui-grid-cell-contents" title="TOOLTIP">{{grid.appScope.cumulative(grid, row)}}</div>'
}
]
};
with the cell template, you could do something like this:
<dt options="options" rows="data" class="material">
<Column name="name" width="300"></Column>
<Column name="Gender">
<strong>{{value}}</strong>
</Column>
<Column name="Company"></Column>
</dt>
notice how I took the React concept and paired it with a more Angular concept where I would include the ability to have a template inside the column.
Ok now for the problem. I want the columns on init, but not after that. I want to replace it with the actual table. Problem is I can never get that HTML when I need it.
So on this line I tried to do something like:
compile: function(tElem, tAttrs){
var tags = z.getElementsByTagName('column');
console.log(tags) // equals = []
return {
pre: function($scope, $elm, $attrs, ctrl){
}
};
}
but the columns are never there til later when I try to transclude them ( not really what I wanna do ). I need a way to get ahold of them before the controller is initialized and the template replaces the inner contents. Here is a plunkr!
Additionally, since my directive is scoped ( which I def wanna do for perf reasons ) I have no way to get access to the parent scope to compile the inner template with the outer contents.
Also, any suggestions / thoughts on this design paradigm? Thanks!
Try to use the template as a function, which returns the actual template string. The first parameter is the original html:
var tags;
return {
template: function(element) {
tags = element[0].getElementsByTagName('column');
return "<div>table</div>"
},
compile: ...
}
I only found a weird way to achieve what you want.
Before someone give a better solution, here is mine working in this plunker.
Adding the $transclude service to the controller and transclude:'element' to the directive
app.directive("simple", function(){
return {
restrict: "EA",
replace:true,
transclude:'element',
template:"<div>table</div>",
compile: function(element, attributes, transclude){
var tags = element[0].getElementsByTagName('column');
console.log("compile", tags);
console.log("transclude", transclude);
return {
pre: function(scope, element, attributes, controller, transcludeFn){
var tags = element[0].getElementsByTagName('column');
console.log("pre", tags);
},
post: function(scope, element, attributes, controller, transcludeFn){
var tags = element[0].getElementsByTagName('column');
console.log("post", tags);
}
}
},
controller: function($scope, $element, $transclude){
$transclude(function(clone,scope){
/* for demo am converting to html string*/
console.log("From controller",angular.element(clone).find('column'));
});
}
};
});
I tested some other things. But i'm only able to get the column with this and only into the controller.
I have the following structure of my grid
$scope.grid = $("#prospectsGrid").kendoGrid({
columns: [
{ template: "<input type='checkbox' class='checkbox' />", width: "3%" },
{ template: "#= FirstName + ' ' + LastName #", title: "Name" },
{ field: "FirstName", hidden: true },
{
command: [
{
name: "edit",
text: "<span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>"
}
}).data("kendoGrid");
As you can see I am accessing this grid with the help of an identifier that is #prospectsGrid. Now without changing any of the functionality inside the grid, I need to replace the identifier (#prospectsGrid) with some angular element. How can I do that? Any help would be appreciated.
Just for reference my HTML Code is
<div id="prospectsGrid" class="gridcommon"></div>
As micjamking says in comments, you need inject in your module KendoUI directives.
angular.module("myApp",["kendo.directives"])
Then you will can use in your html code something like this
<div kendo-grid="myKendoGrid"
k-options="myKendoGridOptions">
</div>
In controller:
angular.module("managerOMKendo").controller("myController", function ($scope) {
// Functions controller
$scope.createMyGrid = function () {
$scope.myKendoGridOptions = {
columns: [ {
field: "FirstName",
template: "<input type='checkbox' class='checkbox' />", width: "3%"
},
command: [{
name: "edit",
text: "<span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>"
}]
// Here you can put your complete configuration. Check official example
}
}
}
// Main thread
$scope.createMyGrid(); // call the function that creates the grid
});
Here is the official example
It´s possible that you need change something. For example where I have written text, maybe you have to write template. It´s hard without plunker.
Good luck!
I have a json object that may be modified by local conditions (not async ajax calls). I want this json object to be output on a datatables grid.
I have the table displaying the data just fine, however if the data changes, the grid does not. Do I have to call the constructor datatables every time? I would prefer to just use 2-way binding, but I am not sure. I have gotten it to update using a $watch, but I'd like to avoid using a $watch on this large object if I can. It could have 1000's of rows.
Here is a fiddle showing the datatable load, and the data it runs on changing after 3000 millis (simple $timeout). Is there a way to get the table to detect the change in the json object and refresh itself? Or do I need to do something different in the Angular code?
I am using a directive to load the table:
.directive('myTable', function() {
return {
restrict: 'E',
scope: {
data: '=',
options: '=',
columns: '='
},
template:
'<table id="balancesTable"></table>',
replace: true,
link: function(scope, elem, attrs) {
scope.options["aaData"] = scope.data;
scope.options["aoColumnDefs"] = scope.columns;
elem.dataTable(scope.options);
}
};
})
My Controller (edited for brevity... see fiddle for full code):
var myApp = angular.module('myApp', [])
.controller('MyAppCtrl', function ($scope, $timeout) {
$("#data-preview").css("color","green");
$scope.dataObj = [ { "balanceType": "Available Funds", ... } ];
$timeout(function(){
$scope.dataObj = [ { "balanceType": "Available Funds", ... different data ...} ];
$("#data-preview").css("color","red");
},
3000);
// columns settings for data table
$scope.columnDefs = [
{
"mData": "balanceType",
"sTitle": "Balance Type",
"aTargets":[0],
"sWidth": "200px"
},
{
...
}
];
// data table settings for table to not show search, pagination and allow column resize
$scope.overrideOptions = {
"bSort": true,
"bPaginate": false,
"sDom": "Rlfrtip",
"bFilter": false,
"bAutoWidth": false,
"bInfo": false
};
})
HTML:
<pre id="data-preview">{{dataObj}} | json}}</pre>
<my-table options="overrideOptions" data="dataObj" columns="columnDefs"><my-table>
Thanks in Advance!!