I need to have a column in ag-grid where i can select multiple values from dropdown. I just googled online to see if it is already implemented but i could find only one link.
https://gist.github.com/gaborsomogyi/00f46f3c0ee989b73c92
Can someone let me know how to implement it. show the full code as an example please.
Here is the code shared over there.
function agDropDownEditor(params, optionsName, optionsList) {
_.set(params.$scope, optionsName+'.optionsList', optionsList);
var html = '<span style="width:100%; display:inline-block" ng-show="!'+optionsName+'.editing" ng-click="'+optionsName+'.startEditing()">{{data.'+params.colDef.field+'}}</span> ' +
'<select style="width:100%" ng-blur="'+optionsName+'.editing=false" ng-change="'+optionsName+'.editing=false" ng-show="'+optionsName+'.editing" ng-options="item for item in '+optionsName+'.optionsList" ng-model="data.'+params.colDef.field+'">';
// we could return the html as a string, however we want to add a 'onfocus' listener, which is not possible in AngularJS
var domElement = document.createElement("span");
domElement.innerHTML = html;
_.set(params.$scope, optionsName+'.startEditing', function() {
_.set(params.$scope, optionsName+'.editing', true); // set to true, to show dropdown
// put this into $timeout, so it happens AFTER the digest cycle,
// otherwise the item we are trying to focus is not visible
$timeout(function () {
var select = domElement.querySelector('select');
select.focus();
}, 0);
});
return domElement;
}
Hope this helps, this is just a snippet of my code what i'm doing is I'm fetching from an array using map and then creating my object which is col and returning it and this will repeat till the last index of that array.
var col = {};
col.field = "fieldName";
col.headerName = "colName";
col.headerCellTemplate = function() {
var eCell = document.createElement('span');
eCell.field = obj.expr;
eCell.headerName = obj.colName;
eCell.innerHTML = "<select>"+"<option>"+
'Abc'+"</option>" +"<option>"+
'Xyz'+"</option>" +"</select>"
//$scope.dropDownTemplate;
var eselect = eCell.querySelector('select');
eselect.focus();
return eCell;
};
return col ;
}));
Related
I am showing data to user using angular ui-grid. In the following code for field 'plan', i add a set of entities by calling the getPlan() function and show it using a cellTemplate in ui-grid columnDefs.
$scope.getPlan = function(rowEntity) {
var plan1Temp = rowEntity.plan1;
var plan2Temp = rowEntity.plan2;
var plan3Temp = rowEntity.plan3;
var plan4Temp = rowEntity.plan4;
var plan5Temp = rowEntity.plan5;
var plan6Temp = rowEntity.plan6;
var plan7Temp = rowEntity.plan7;
var plan8Temp = rowEntity.plan8;
plan = plan1Temp+plan2Temp+plan3Temp+plan4Temp+plan5Temp+plan6Temp+plan7Temp+plan8Temp;
console.log("plan=", plan);
return plan;
};
columnDefs: [{ field : 'plan',
cellTemplate : '<div id="grid-cell-template">
{{grid.appScope.getPlan(row.entity)}}</div>',
displayName : "Plan",
footerCellTemplate : '<div class="ui-grid-cell-contents">
\Total:\ {{grid.appScope.getTotalPlan() }}</div>',
width : "*"
}]
Then to show the total of all the plans i use footerCellTemplate with getTotalPlan(). While using it i got $digest() iterations reached error. I noticed the getPlan() function is called many times. So i added $watch like shown below.
var totalPlan = [];
$scope.$watch(function() {
return plan;
}, function() {
totalPlan.push(plan);
console.log("totalPlan =", totalPlan);
});
$scope.getTotalPlan = function() {
var sum = 0;
angular.forEach(totalPlan ,function(item){
sum = sum + item;
});
return sum;
console.log("getTotalPlan=", sum);
};
After adding $watch also the getPlan() function is called many times. Also in totalPlan[] it holds only 2 values, first null and second one last record value(in total there are 3 records) and prints this value in the ui-grid footercelltemplate. How should i use $watch to make it work right?
So basically the problem is i am not able to insert 'plan' values into totalPlan[ ] and show the total in the grid footer template.
Also i tried using :: inside the expression, the number of getPlan() calling times reduced to half but no luck.
I am using a ContextMenu directive within a kendo grid. I have made one change to it so I can include icons in the text (changed $a.text(text) to $a.html(text).
I have one in the first cell (I highjacked the hierarchical cell) that has row operations (add, clone& delete) and one on a span within each cell that changes the cell values operation (addition, subtraction, equals, etc...)
Both of these were working. I am unsure what I changed that stopped it from working because I last checked it several changes ago (I'm still locked out of TFS so I can't revert).
One change I made was to include a disabled/enabled check to the working contextMenu. I tried adding the same to the broken one and no dice.
I do perform a $compile on the working menu and the broken one is only included in the kendo field template.
If I must compile the field template (and I didn't need to before), how can this be done?
So here is some code.
working menu:
$scope.getRowContextMenu = function (event) {
var options =
[[
"<span class='fa fa-files-o'></span>Clone Rule", function (scope, cmEvent) {/*omitted for brevity*/}),rowContextDisableFunction]]
}
var setHierarchyCell = function (grid) {
var element = grid.element;
var hCells = element.find("td.k-hierarchy-cell");
hCells.empty();
var spanStr = "<span context-menu='getRowContextMenu()' class='fa fa-bars'></span>";
hCells.append($compile(spanStr)($scope));
var span = hCells.find("span.fa");
span.on('click', function (event) {
$(this).trigger('contextmenu', event);
});
}
kendo template:
var mutliFormTemplate = function (fieldName, type) {
var result = "";
result += "<span context-menu='getOperationContextMenuItems()' class='fa #= " + fieldName + "_Obj.OperationSymbol # type-" + type + "'> </span>\n";
/*The rest pertains to the cell value. excluded for brevity*/
return result;
}
$scope.getOperationContextMenuItems = function () {
//I trimmed this all the way down to see if I could get it working. Still no joy
return [
["test", function () { }, true]
];
}
Creating the kendo columns dynamically:
$scope.model = {
id: "RuleId",
fields: {}
};
$scope.fieldsLoaded = function (data, fields) {
var column = {}
$.each(fields, function () {
var field = this;
$scope.columns.push({
field: field.Name,
title: field.Name,
template: mutliFormTemplate(field.Name, "selector")
});
column[field.Name ] = { type: getFieldType(field.Type.BaseTypeId) }
});
$scope.model.fields = column;
}
Thanks for any and all help ^_^
Seems like a simple problem though but finding it hard to fix.
There is a pagination component, that has a button & a dropdown. User can go to a page by either clicking the button or selecting that page number in dropdown.
The problem is, when I select a value in the dropdown, nothing happens. Because the scope variable doesnt change from the previous one.
aspx:
<div data-ng-app="app" data-ng-controller="ReportsCtrl">
<div id="paging-top">
<div>
<ul>
<li>
<select data-ng-model="SelectedPage" data-ng-change="ShowSelectedPage();"
data-ng-options="num for num in PageNumbers track by num">
</select>
</li>
<li data-ng-click="ShowNextPage();">Next</li>
</ul>
</div>
</div>
app.js
var app = angular.module("app", ["ngRoute"]);
ReportsCtrl.js
app.controller("ReportsCtrl", ["$scope","ReportsFactory",function ($scope,ReportsFactory) {
init();
var init = function () {
$scope.ShowReport(1);
}
$scope.ShowReport = function (pageNumber) {
GetUserResponsesReport(pageNumber);
}
function GetUserResponsesReport(pageNumber) {
$scope.UserResponsesReport = [];
var promise = ReportsFactory.GetReport();
promise.then(function (success) {
if (success.data != null && success.data != '') {
$scope.UserResponsesReport = success.data;
BindPageNumbers(50, pageNumber);
}
});
}
function BindPageNumbers(totalRows, selectedPage) {
$scope.PageNumbers = [];
for (var i = 1; i <= 5 ; i++) {
$scope.PageNumbers.push(i);
}
$scope.SelectedPage = selectedPage;
}
$scope.ShowSelectedPage = function () {
alert($scope.SelectedPage);
$scope.ShowReport($scope.SelectedPage);
}
$scope.ShowNextPage = function () {
$scope.SelectedPage = $scope.SelectedPage + 1;
$scope.ShowReport($scope.SelectedPage);
}
}]);
Say, the selected value in dropdown is 1. When I select 2 in the dropdown, the alert shows1. When Next is clicked, the dropdown selection changes to 2 as expected. Now, when I select 1 in the dropdown, the alert shows 2.
Tried to make a fiddle, but do not know how to do with a promise - http://jsfiddle.net/bpq5wxex/2/
With your OP SelectedPage is just primitive variable.
With every angular directive new scope is get created.
So,SelectedPage is not update outside the ng-repeat scope after drop-down is changed i.e. in parent scope which is your controller.
In order to do this,use Object variable instead of primitive data types as it update the value by reference having same memory location.
Try to define SelectedPage object in controller in this way.
$scope.objSelectedPage = {SelectedPage:''};
in HTML
<select data-ng-model="objSelectedPage.SelectedPage" data-ng-change="ShowSelectedPage();"
In ShowSelectedPage
$scope.ShowSelectedPage = function () {
console.log($scope.objSelectedPage.SelectedPage);
$scope.ShowReport($scope.objSelectedPage.SelectedPage);
}
I'm building a CRUD data management project using Angular and the dirPagination directive and need to have a "select all" checkbox that selects all rows that are visible /without/ using jQuery.
I started with this - note - I am climbing the Angular learning curve and am aware that some/all of this may not be "the Angular way" but I also don't want to start fiddling (no pun) with the guts of dirPagination, ergo the dirPagination directive:
<tr dir-paginate-start="item in items|filter:filterFunction()|orderBy:sortPredicate:reverse| itemsPerPage: rowsPerPage">
and the individual row-checkbox
<input type="checkbox" class="rowSelector" ng-model="item.isSelected" ng-change="rowSelect($index, $event)"/> status: {{item.isSelected}}
and the related model elements:
$scope.items = [] //subsequently filled
$scope.rowsPerPage = 5;
$scope.rowSelect = function (ix, $event) {
var checked = (typeof $event == 'undefined') ? false : true;
if (!checked) { $scope.masterCheck = false; }
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
$scope.items[start + ix].isSelected = checked;
};
that works as expected. Check/uncheck a row and the {{item.isSelected}} value is updated in the model and is displayed beside the checkbox.
Then I added this /outside/ of the dirPagination repeat block:
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
and the related function in the model:
$scope.masterCheck = false;
$scope.checkAll = function () {
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
var checked = $scope.masterCheck == true;
var rows = document.getElementsByClassName("rowSelector");
for (var ix = 0; ix < rows.length; ix++) {
rows[ix].checked = checked;
$scope.items[start + ix].isSelected = checked;
}
}
however in the checkAll() function checking/unchecking the individual rows isn't reflected in the {{item.isSelected}} display of each row.
Explicitly setting the individual item with
$scope.items[start + ix].isSelected = checked;
seems to set the 'isSelected' property of that item within the scope of the checkAll function but the row display does not change.
Clearly I have something wrong perhaps misunderstanding a Scope issue but at this point I'm stumped.
Any help greatly appreciated :-)
The light dawned, finally.
checkAll() as written tried to access each row by calculating its position using dir-paginate's __default__currentPage and Angular's row $index.
Of course that doesn't work because the items[] collection held by dir-paginate has been subjected to filtering and sorting, so while items[] do get checked (item.isSelected = true) the selected items/rows were living on non-visible pages. i.e. - we were selecting the wrong indexes.
One solution is comprised of the following -
The master checkbox
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
The row checkbox (note function calls)
<input type="checkbox" class="rowSelector" value="{{sourceIndex('senId',item.senId)}}" ng-model="item.isSelected" ng-click="rowSelect(this)" />
the dir-paginate directive controls tag
<dir-pagination-controls on-page-change="onPageChange(newPageNumber)" max-size="15" direction-links="true" boundary-links="true" pagination-id="" template-url=""></dir-pagination-controls>
and the related $scope values and functions:
$scope.items = [];
$scope.masterCheck = false;
$scope.onPageChange = function (newPageNumber) {
//clear all selections on page change
//dir-paginate provides this hook
$scope.masterCheck = false;
for (var i in $scope.items) {
$scope.items[i].isSelected = false;
}
}
$scope.rowSelect = function (item) {
//if one is unchecked have to turn master off
if (!item.isSelected) $scope.masterCheck = false;
}
$scope.sourceIndex = function (keyName, key) {
//gets the actual items index for the row key
//see here http://stackoverflow.com/questions/21631127/find-the-array-index-of-an-object-with-a-specific-key-value-in-underscore
//for the 'getIndexBy' prototype extension
var ix = $scope.items.getIndexBy(keyName, key);
return ix;
}
$scope.checkAll = function () {
//only visible rows
var boxes = document.getElementsByClassName("rowSelector");
for (var bix in boxes) {
var ix = boxes[bix].value;
$scope.items[ix].isSelected = $scope.masterCheck;
}
}
There is probably a better way but while not highly efficient this one works well enough for common folk.
The $scope.sourceIndex() function stuffs the actual source row index into the row checkbox as its value= attribute value.
The checkAll() function then grabs all visible rows by the "rowSelector" class and then iterates through those grabbing the data index from the checkbox value and setting the appropriate item.isSelected.
The $scope.onPageChange is specified in the dir-Paginate controls directive and ensures that when the page changes, all row selections are cleared.
Happy happy.
Your variable masterCheck is being set to false in rowSelect() and subsequently in checkAll() you are setting checked to masterCheck which then is used to assign isSelected
This line is wrong:
var checked = $scope.masterCheck == true;
Because you want to flip masterCheck so it should be:
$scope.masterCheck = !$scope.masterCheck;
and then
.isSelected = $scope.masterCheck;
You weren't ever setting $scope.masterCheck to true so it was always false and since your isSelected values depended on it, they were always false. Also, this functions as a checkAll/unCheckAll to make it only check all change to the following:
$scope.masterCheck = !$scope.masterCheck;
var checked = $scope.masterCheck == true;
I would like to wrap text with span tag and save the position.
I know how to do it with JS but i dont know how to do it with angularjs
Here is what i have done:
http://jsfiddle.net/ymeaL06j/1/
This function gives me the position of the text in the DIV
function getSelectionPosition() {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(document.getElementById("code"));
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
};
I take the start and end positions and insert them as an attribute in the span tag
After this i would like to save all the marked positions and load it later, i have a function that select text and then i can wrap it (i hope that there is a better solution)
function setSelection(savedSel) {
var charIndex = 0, range = document.createRange();
range.setStart(document.getElementById("code"), 0);
range.collapse(true);
var nodeStack = [containerEl], node, foundStart = false, stop = false;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
You can use the same code that you illustrated above and put it inside of your angularjs controller.
Refer to my plunker code; it is a simple angularjs version of your jsfiddle code.
For example, suppose that a snippet of the index.html looks like this:
<body ng-controller="MainCtrl">
<div id="code">This is <b>some text</b> bla bla bla</div>
<br />
<input type="button" value="Mark!" ng-click="markText()" />
<input type="button" value="Remove marks!" ng-click="removeMarks()" />
</body>
Then the example angularjs controller, MainCtrl, could look like this:
app.controller('MainCtrl', function($scope) {
var getSelectionPosition = function () {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(document.getElementById("code"));
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
}
$scope.markText = function() {
var currPosition = getSelectionPosition();
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.className = "Mark";
span.setAttribute("PosStart", currPosition.start);
span.setAttribute("PosEnd", currPosition.end);
span.appendChild(selectedText);
selection.insertNode(span);
};
$scope.removeMarks = function() {
$(".Mark").each(function () {
$(this).contents().unwrap();
});
};
});
Notice that the MainCtrl is the angularjs controller for the body. The ng-click on the buttons reference the markText and removeMarks functions in the controller's scope. The logic in the functions are exactly the same as you referenced in your question (and jsfiddle).
None of your JS code changed other than moving the functions inside of the controller. Again, check out the plunker above to see the actual code working.