Angularjs UI-Grid Right click is no longer working? - angularjs

I have a simple UI-Grid application that defines a grid like this
<div ui-grid="gridOptions" ui-grid-pagination class="grid clsVendorsGrid" ui-grid-selection ui-grid-exporter right-click="rightClick($event)" context-menu="menuOptions" context-menu-class="custom_class"></div>
And then the right click event is like this
$scope.rightClick = function (event) {
$scope.gridApi.selection.clearSelectedRows();
var element = angular.element(event.toElement);
var id = element[0].parentElement.id;
This was working fine for the past few years. But since last week the event is now always null. What gives ?
I am using angular-ui-grid version 3.2.9
The error I get on right click is
TypeError: Cannot read property 'parentElement' of undefined
at m.$scope.rightClick (administrativeController.js:281)
Appreciate any inputs on this
EDIT:
I am trying to get the ID on right click, I am binding the ID like this
onRegisterApi: function (gridApi) {
$scope.gridApi = gridApi;
//Register this to Capture Selected Item on right click
gridApi.selection.on.rowSelectionChanged($scope,
function (row) {
if (row.isSelected) {
$scope.selectedId = row.entity.iTempId;
$scope.selectedVendor = row.entity.sBusinessnameLegal;
}
});
}

You just need to change event.toElement to event.target:
$scope.rightClick = function (event) {
var element = angular.element(event.target);
var id = element[0].parentElement.id;
...
}
Here is working snippet:
angular.module("ng-context-menu",[]).factory("ContextMenuService",function(){return{element:null,menuElement:null}}).directive("contextMenu",["$document","ContextMenuService",function(e,n){return{restrict:"A",scope:{callback:"&contextMenu",disabled:"&contextMenuDisabled",closeCallback:"&contextMenuClose"},link:function(t,l,c){function o(n,t){t.addClass("open");var l=e[0].documentElement,c=(window.pageXOffset||l.scrollLeft)-(l.clientLeft||0),o=(window.pageYOffset||l.scrollTop)-(l.clientTop||0),u=t[0].scrollWidth,i=t[0].scrollHeight,a=l.clientWidth+c,d=l.clientHeight+o,p=u+n.pageX,s=i+n.pageY,r=Math.max(n.pageX-c,0),f=Math.max(n.pageY-o,0);p>a&&(r-=p-a),s>d&&(f-=s-d),t.css("top",f+"px"),t.css("left",r+"px"),m=!0}function u(e){e.removeClass("open"),m&&t.closeCallback(),m=!1}function i(e){!t.disabled()&&m&&27===e.keyCode&&t.$apply(function(){u(n.menuElement)})}function a(e){t.disabled()||!m||2===e.button&&e.target===n.element||t.$apply(function(){u(n.menuElement)})}var m=!1;l.bind("contextmenu",function(e){t.disabled()||(null!==n.menuElement&&u(n.menuElement),n.menuElement=angular.element(document.getElementById(c.target)),n.element=e.target,e.preventDefault(),e.stopPropagation(),t.$apply(function(){t.callback({$event:e})}),t.$apply(function(){o(e,n.menuElement)}))}),e.bind("keyup",i),e.bind("click",a),e.bind("contextmenu",a),t.$on("$destroy",function(){e.unbind("keyup",i),e.unbind("click",a),e.unbind("contextmenu",a)})}}}]);
var app = angular.module('app', ['ui.grid','ui.grid.selection']);
app.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
app.controller('MainCtrl', ['$scope', '$interval', function ($scope, $interval) {
var myData = [
{
"firstName": "Cox",
"lastName": "Carney",
"company": "Enormo",
"employed": true,
iTempId: 1,
sBusinessnameLegal:"sBusinessnameLegal1"
},
{
"firstName": "Lorraine",
"lastName": "Wise",
"company": "Comveyer",
"employed": false,
iTempId: 2,
sBusinessnameLegal: "sBusinessnameLegal2"
},
{
"firstName": "Nancy",
"lastName": "Waters",
"company": "Fuelton",
"employed": false,
iTempId: 3,
sBusinessnameLegal: "sBusinessnameLegal3"
}];
$scope.gridOptions = {
data: myData,
enableRowSelection: true,
onRegisterApi: function (gridApi) {
$scope.gridApi = gridApi;
gridApi.selection.on.rowSelectionChanged($scope,
function (row) {
if (row.isSelected) {
debugger
$scope.selectedId = row.entity.iTempId;
$scope.selectedVendor = row.entity.sBusinessnameLegal;
}
});
}
};
$scope.rightClick = function (event) {
var element = angular.element(event.target);
//get cellId which should look like
//1464688691229-2-uiGrid-0006-cell
var id = element[0].parentElement.id;
var regex = /(\d+)/g
var result = id.match(regex);
var rowIndex = parseInt(result[1]); //extract second numic match
// console.log(id);
//console.log("rowIndex=%d", rowIndex);
$scope.gridApi.selection.selectRowByVisibleIndex(rowIndex);
};
}]);
app.directive('rightClick', function ($parse) {
return function (scope, element, attrs) {
var fn = $parse(attrs.rightClick);
element.bind('contextmenu', function (event) {
scope.$apply(function () {
event.preventDefault();
fn(scope, { $event: event });
});
});
};
});
<!doctype html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/csv.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/pdfmake.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/3.1.1/ui-grid.min.js"></script>
<script src="ng-context-menu.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/3.1.1/ui-grid.min.css" type="text/css">
<link rel="stylesheet" href="main.css" type="text/css">
</head>
<body>
<div ng-controller="MainCtrl">
<p>selectedId: {{selectedId}}</p>
<p>selectedVendor: {{selectedVendor}}</p>
<div id="grid1" ui-grid="gridOptions" ui-grid-selection right-click="rightClick($event)" class="grid"></div>
</div>
</body>
</html>

Related

How can I mouse select one column in Angular UI Gird?

For example, in below very simple Angular UI Gird sample, you can NOT select a specific column. When click mouse and drag, it will select both rows and columns, is there a way to only select a column?
http://embed.plnkr.co/BjLqXGiUI8nQFwvijduh
Also, no matter what you select, if you copy and paste into excel, the data is always in one column with no proper format. Does anyone know how to fix this? Thanks!!!
JS:
var app = angular.module('app', ['ui.grid', 'ui.grid.cellNav']);
app.controller('MainCtrl', [
'$scope',
'$http',
'$log',
'$timeout',
'uiGridConstants',
function($scope, $http, $log, $timeout, uiGridConstants) {
$scope.gridOptions = {
modifierKeysToMultiSelectCells: true,
enableRowSelection: false,
enableSelectAll: true,
// selectionRowHeaderWidth: 35,
rowHeight: 35,
showGridFooter: true,
};
$scope.gridOptions.columnDefs = [
{ name: 'name' },
{ name: 'amount', displayName: 'Amount', allowCellFocus: false },
];
$scope.gridOptions.multiSelect = true;
$http.get('data.json').success(function(data) {
$scope.gridOptions.data = data;
});
$scope.info = {};
$scope.captureCopy = function(e) {
console.log('captured keyboard event.');
if ((e.keyCode === 99 || e.keyCode === 67) && e.ctrlKey) {
console.log('copying...');
}
};
$scope.gridOptions.onRegisterApi = function(gridApi) {
//set gridApi on scope
$scope.gridApi = gridApi;
};
},
]);
app
.controller('SecondCtrl', [
'$scope',
'$http',
'$interval',
'uiGridConstants',
function($scope, $http, $interval, uiGridConstants) {
$scope.gridOptions = {
enableRowSelection: true,
enableRowHeaderSelection: false,
};
$scope.gridOptions.columnDefs = [
{ name: 'id' },
{ name: 'name' },
{
name: 'age',
displayName: 'Age (not focusable)',
allowCellFocus: false,
},
{ name: 'address.city' },
];
$scope.gridOptions.multiSelect = false;
$scope.gridOptions.modifierKeysToMultiSelect = false;
$scope.gridOptions.noUnselect = true;
$scope.gridOptions.onRegisterApi = function(gridApi) {
$scope.gridApi = gridApi;
};
$scope.toggleRowSelection = function() {
$scope.gridApi.selection.clearSelectedRows();
$scope.gridOptions.enableRowSelection = !$scope.gridOptions
.enableRowSelection;
$scope.gridApi.core.notifyDataChange(
uiGridConstants.dataChange.OPTIONS
);
};
$http.get('data.json').success(function(data) {
$scope.gridOptions.data = data;
// $interval whilst we wait for the grid to digest the data we just gave it
$interval(
function() {
$scope.gridApi.selection.selectRow($scope.gridOptions.data[0]);
},
0,
1
);
});
},
])
.directive('uiGridCellSelection', function($compile) {
/*
Proof of concept, in reality we need on/off events etc.
*/
return {
require: 'uiGrid',
link: function(scope, element, attrs, uiGridCtrl) {
// Taken from cellNav
//add an element with no dimensions that can be used to set focus and capture keystrokes
var gridApi = uiGridCtrl.grid.api;
var focuser = $compile(
'<div class="ui-grid-focuser" tabindex="-1"></div>'
)(scope);
element.append(focuser);
uiGridCtrl.focus = function() {
focuser[0].focus();
};
gridApi.cellNav.on.viewPortKeyDown(scope, function(e) {
if ((e.keyCode === 99 || e.keyCode === 67) && e.ctrlKey) {
var cells = gridApi.cellNav.getCurrentSelection();
var copyString = '',
rowId = cells[0].row.uid;
angular.forEach(cells, function(cell) {
if (cell.row.uid !== rowId) {
copyString += '\n';
rowId = cell.row.uid;
}
copyString += gridApi.grid
.getCellValue(cell.row, cell.col)
.toString();
copyString += ', ';
});
// Yes, this should be build into a directive, but this is a quick and dirty example.
var textArea = document.getElementById('grid-clipboard');
textArea.value = copyString;
textArea = document.getElementById('grid-clipboard').select();
}
});
focuser.on('keyup', function(e) {});
},
};
})
.directive('uiGridClipboard', function() {
return {
template:
'<textarea id="grid-clipboard" ng-model="uiGridClipBoardContents"></textarea>',
replace: true,
link: function(scope, element, attrs) {
// Obviously this needs to be hidden better (probably a z-index, and positioned behind something opaque)
element.css('height', '1px');
element.css('width', '1px');
element.css('resize', 'none');
},
};
});
HTML:
<!doctype html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.js"></script>
<script src="//cdn.rawgit.com/angular-ui/bower-ui-grid/v3.0.6/ui-grid.min.js"></script>
<link rel="stylesheet" href="//cdn.rawgit.com/angular-ui/bower-ui-grid/v3.0.6/ui-grid.min.css" type="text/css" />
<link rel="stylesheet" href="main.css" type="text/css">
</head>
<body>
<div ng-controller="MainCtrl" >
<p>Select using ctrl-click. To select many: press ctrl+click in the cell you want to mark and paste it into MS excel. Note that you must select the cells int the right order, since the cellNav api returns them in the selection order, not the order they appear in the grid.</p>
<br/>
<div ui-grid="gridOptions" ui-grid-cell-selection ui-grid-cellNav class="grid"></div>
<div ui-grid-clipboard ng-keydown="captureCopy($event)"></div>
</div>
<script src="app.js"></script>
</body>
</html>
Can copy cells and be pasted into excel. You should be able to work out what you need from this. Otherwise I can take a quick look at it tomorrow again. Hope it helps!

How to display Ui-grid after data getting from services?

This is my code
var app = angular.module('app', ['ngTouch', 'ui.grid']);
app.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {
var today = new Date();
$scope.gridOptions = {
enableFiltering: false,
onRegisterApi: function(gridApi){
$scope.gridApi = gridApi;
$scope.gridApi.grid.registerRowsProcessor( $scope.singleFilter, 200 );
},
columnDefs: [
{ field: 'name' },
{ field: 'gender', cellFilter: 'mapGender' },
{ field: 'company' },
{ field: 'email' },
{ field: 'phone' },
{ field: 'age' },
{ field: 'mixedDate' }
]
};
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json')
.success(function(data) {
$scope.gridOptions.data = data;
$scope.gridOptions.data[0].age = -5;
data.forEach( function addDates( row, index ){
row.mixedDate = new Date();
row.mixedDate.setDate(today.getDate() + ( index % 14 ) );
row.gender = row.gender==='male' ? '1' : '2';
});
});
$scope.filter = function() {
$scope.gridApi.grid.refresh();
};
$scope.singleFilter = function( renderableRows ){
var matcher = new RegExp($scope.filterValue);
renderableRows.forEach( function( row ) {
var match = false;
[ 'name', 'company', 'email' ].forEach(function( field ){
if ( row.entity[field].match(matcher) ){
match = true;
}
});
if ( !match ){
row.visible = false;
}
});
return renderableRows;
};
}])
.filter('mapGender', function() {
var genderHash = {
1: 'male',
2: 'female'
};
return function(input) {
if (!input){
return '';
} else {
return genderHash[input];
}
};
});
<!doctype html>
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-touch.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-animate.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/csv.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/pdfmake.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js"></script>
<script src="http://ui-grid.info/release/ui-grid.js"></script>
<link rel="stylesheet" href="http://ui-grid.info/release/ui-grid.css" type="text/css">
<link rel="stylesheet" href="main.css" type="text/css">
</head>
<body>
<div ng-controller="MainCtrl">
<input ng-model='filterValue'/><button ng-click='filter()'>Filter</button>
<div id="grid1" ui-grid="gridOptions" class="grid"></div>
</div>
<script src="app.js"></script>
</body>
</html>
In the above code first ui-grid structure display then after data will bind to the ui-grid, but i want to display entire grid with structure after loading data.
this is my plunker http://plnkr.co/edit/?p=preview
A popular design is to show loading status when data is not yet loaded, to do so, you could add a dataLoaded flag to control when the grid is ready, set the flag as true in the success callback of $http.get. See the below:
$scope.dataLoaded = false;
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json').success(function(data) {
// ...
$scope.dataLoaded = true;
});
and in your template, it could be as:
<div id="grid1" ui-grid="gridOptions" class="grid" ng-show="dataLoaded"></div>
<div ng-hide="dataLoaded">Loading...</div>

handsontable in an angularjs directive - render an anchor that has an ng-click

So I'm using Handsontable to render a grid. (Yes, I am NOT using the ngHandsontable. I started out with that but ran into problems and so I went with just rendering a Handsontable from an angularjs directive.)
I want one column to hold an anchor tag.
I want the anchor tag to have the angularjs ng-click directive.
Everything renders correctly but the ng-click is not called.
Here is my example.
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.doNgClick = function() {
alert('ng-click');
// console.log('ng-click');
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='doNgClick()'>Test</a>"
// test: "<a ng-click='doNgClick()'>Test</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '='
};
directive.link = function(scope,element,attrs) {
var container = $(element);
// var safeHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
// var escaped = Handsontable.helper.stringify(value);
// td.innerHTML = escaped;
// return td;
// };
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data: "test",
renderer: "html",
// renderer: safeHtmlRenderer,
readyOnly: true
}
]
};
var hot = new Handsontable( container[0], settings );
hot.render();
// console.log(element.html());
// $compile(element.contents())(scope);
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="//handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>
After much reading and digging here is my own answer.
//-- With help from the following:
//--
//-- http://stackoverflow.com/questions/18364208/dynamic-binding-of-ng-click
//-- http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters
//--
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.click = function(msg) {
console.log('ctrl_doNgClick: ng-click: msg: '+msg);
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='dir_ctrl_click(\"blah1,blah1\")'>Test 1</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah2,blah2\")'>Test 2</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah3,blah3\")'>Test 3</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '=',
click : '&'
};
directive.controller = function($scope) {
$scope.dir_ctrl_click = function( msg ) {
console.log('controller: dir_ctrl_click: click via the directive controller method');
$scope.click()(msg);
};
};
directive.link = function(scope,element,attrs) {
var container = $(element);
scope.doClick = function(msg) {
console.log('link: doClick: click via the directive link method');
scope.click()(msg);
};
var linkHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
//-- here is the magic that works
//-- the method, in ng-click, must either be defined here in the link method or in the controller method (the example data contains both)
var el = angular.element(td);
el.html($compile(value)(scope));
return el;
};
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data : "test",
renderer : linkHtmlRenderer,
readyOnly : true
}
]
};
var hot = new Handsontable( container[0], settings );
// hot.render();
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple" click="click"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="http://handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>

Mithril with angularjs

I am a newbie to Mithril JS framework and trying to integrate Mitril view with angularJS. Has anyone tried this before?
I want to check how can we bind the angular controller methods to click events of elements created in Mitril.
I got this working by having this code
var e = document.getElementById('elementId');
var scope = angular.element(e).scope();
m("a[href='javascript:;']", {
onclick : scope.someMethod
}, "Test");
But I am not sure if this is right way to do this.
I'd say that is not idiomatic angular code.
A more idiomatic way might be to use a directive on the Angular side, and pass in an event dispatcher controller to the view on the mithril side:
//mithril code
var testWidget = function(ctrl) {
return m("a[href='javascript:;']", {onclick: ctrl.onclick}, "Test")
}
//angular code
angular.module("foo").directive("testWidget", function() {
return {
restrict: "E",
link: function($scope, element, attrs) {
var template = testWidget({
onclick: function() {
$scope.$apply(function() {
$scope.$eval(attrs.onclick)
})
}
})
m.render(element, template)
}
}
})
angular.module("foo").controller("MyCtrl", function() {
this.doStuff = function() {
console.log("called doStuff")
}
})
<div ng-controller="MyCtrl as c">
<test-widget onclick="c.doStuff()"></test-widget>
</div>
// Code goes here
(function() {
'use strict';
angular
.module('app', [])
.directive('testMithrilScope', testMithrilScope)
.controller('MyCtrl', MyCtrl);
var testMithrilWidgetScope = function(ctrl) {
return m("a[href='javascript:;']", {onclick: ctrl.directiveclick}, ctrl.text)
}
var htmllinks = [
{text: "Link 1 "},
{text: "Link 2 "},
{text: "Link 3 "},
{text: "Link 4 "},
{text: "Link 5 "},
{text: "Link 6 "}
];
function testMithrilScope() {
return {
restrict: "E",
scope : {
htmlclick: '&'
},
link: function($scope, element, attrs) {
function makeList1() {
return m('ul', htmllinks.map(function(a, index){
return m('li', testMithrilWidgetScope({
directiveclick : function() {
var data = {
arg1: a.text
}
$scope.htmlclick(data);
},
text : a.text
})
);
}));
}
var template1 = makeList1();
m.render(element[0], template1)
}
}
}
function MyCtrl() {
this.doStuff = function(text) {
console.log("You clicked: " + text)
}
}
})();
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.5.8" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<script data-require="mithril#0.2.4" data-semver="0.2.4" src="https://cdn.jsdelivr.net/mithril/0.2.4/mithril.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="MyCtrl as ctrl">
<test-mithril-scope htmlclick="ctrl.doStuff(arg1)"></test-mithril-scope>
</div>
</body>
</html>

Multiple custom filters in angular.js

I have a multi check box application which requires me to have multiple filters. I have been unable to use multiple filters even if I try to hard code an array to filter on. Here is an example I have created to try to make this work.
Working Example
HTML MARKUP:
<body ng-app="app">
<div ng-controller="MainCtrl">
<div ng-repeat="item in data.sessions | IndustryFilter : data.sessions.industry ">
{{item.id}}
</div>
</div>
Javascript
var app = angular.module("app", [])
.controller("MainCtrl", function ($scope, MatchedFilterList) {
$scope.data = {"sessions": [{
"id": "a093000000Vhzg7AAB",
"industry": ["Automovtive"],
"sessionName": "Keynote",
},
{
"id": "a093000000zg7AAB",
"industry": ["Automovtive", "Retail"],
"sessionName": "Keynote2",
},
{
"id": "a093er000f00zg7AAB",
"industry": ["Automovtive", "Retail", "Consumer Goods"],
"sessionName": "Keynote3",
}
]};
}).filter("IndustryFilter", function (MatchedFilterList) {
return function () {
var filtered = [];
angular.forEach(MatchedFilterList.industry, function (item) {
filtered.push(item);
});
console.log("Filter: Filter " + filtered)
return filtered;
};
})
.factory("MatchedFilterList", function(){
var matchedFilterList = {};
matchedFilterList.industry = {
"Automotive": "Automotive",
"Retail" : "Retail"
};
return matchedFilterList;
});

Resources