Hide column from grid while exporting to excel - kendo-ui

I want to hide/remove one column when the data exported to excel..But that should be visible in grid. I have tried several solutions but not able find out the exact problem.
Currently excel is generating but unable to hide the column... Can anybody please help regarding this ?? Thanks in advance.
Here is my grid code:
#(Html.Kendo().Grid<Database.Model.UserSummaryInfo>()
.Name("Grid")
.Columns(col =>
{
col.Bound(c => c.ApplicationUserId).Hidden();
col.Bound(c => c.MemberId).Title("Member ID");
col.Bound(c => c.Visit).Title("Visit");
col.Bound(c => c.CreatedDate).Title("Visit Start Date");
col.Bound(c => c.LogInCount).Title("LogIn Count");
col.Bound(c => c.SurveyStatus).Title(" Survey Status");
col.Bound(c =>
c.ApplicationUserId).HeaderTemplate(#<text>Action</text>).ClientTemplate("#
if(SurveyStatus == 'Did Not Attempt') { #" + "<a class='btn btn-primary
disabled' style='display: none;' href='" + Url.Action("TestDetails",
"Admin") + "?id=#= TestSummaryId #&Year=#=Year#'" + " >Details</a>" + "#
}else{#" + "<a class='btn btn-primary enabled' style='width:60px' href='"
+ Url.Action("TestDetails", "Admin") + "?id=#= ApplicationUserId
#&Year=#=Year #&testSummaryId=#=TestSummaryId#'" + ">Details</a>" + "#
}#")
.HeaderHtmlAttributes(new { style = "text-align: center;font-size:18px"
});
})
.ToolBar(toolbar => toolbar.Template(#<text>
<div class="pull-left index-header">Test Summary</div>
<button type="button" class="btn btn-primary rounded pull-
right margin-right-10" onclick="clearFiter()"><i class="fa fa-times-
circle-o margin-right-5"></i> Clear Filter</button>
<a style="padding-right:5px;" class="k-button-icontext k-grid-
excel btn btn-primary pull-right margin-right-10" href="#"><span
class="fa fa-file-excel-o"></span>Export to Excel</a>
</text>))
.Excel(excel => excel
.FileName(DateTime.Now.Date.ToShortDateString() + " " +
"GetUserSummary.xlsx")
.AllPages(false)
.ProxyURL(Url.Action("Excel_Export_Save", "Admin")))
.Pageable(paging => paging.PageSizes(new int[] { 100, 500, 1000
}).Refresh(true).ButtonCount(5).Info(true).Input(true))
.Sortable(sortable =>
{
sortable.SortMode(GridSortMode.SingleColumn);
})
.Groupable()
.Scrollable(s => s.Height("auto"))
.Filterable(filterable => filterable.Operators(operators =>
operators.ForNumber(nmbr => nmbr.Clear().IsEqualTo("Is equal
to").IsLessThan("Less than").IsGreaterThan("Greater
than").IsNotEqualTo("Not equal to")).ForString(str =>
str.Clear().Contains("Contains").IsEqualTo("Is equal
to").StartsWith("Starts with").IsNotEqualTo("Is not equal
to")).ForDate(date => date.Clear().IsGreaterThan("Is
after").IsLessThan("Is Before").IsGreaterThanOrEqualTo("Is after or
equal to").IsLessThanOrEqualTo("Is before or equal to"))))
.Resizable(resize => resize.Columns(true))
.Events(e => e.ExcelExport("Hidecolumn"))
.DataSource(datasource =>
datasource
.Ajax()
.Sort(sort => {
sort.Add(c => c.MemberId).Ascending();
sort.Add(c => c.Visit).Ascending();
})
.PageSize(10)
.Read(read => read.Action("GetUserSummaryList", "Admin"))
)
)
</div>
</div>
</div>
<!-- End Content -->
</form>
</div>
<script>
var exportFlag = false;
$("#Grid").data("kendoGrid").bind("excelExport", function (e) {
debugger;
if (!exportFlag) {
e.sender.hideColumn(2);
e.preventDefault();
exportFlag = true;
setTimeout(function () {
e.sender.saveAsExcel();
});
} else {
e.sender.showColumn(2);
exportFlag = false;
}
});
function Hidecolumn(e) {
e.sender.hideColumn(2);
}
</script>

What you would need to do is to bind to the export function and then hide the columns similar to how you have above:
var exportFlag = false;
$("#grid").data("kendoGrid").bind("excelExport", function (e) {
if (!exportFlag) {
e.sender.hideColumn(1);
e.preventDefault();
exportFlag = true;
setTimeout(function () {
e.sender.saveAsExcel();
});
} else {
e.sender.showColumn(1);
exportFlag = false;
}
});
The exportFlag is due to stopping a recursive loop, because the saveAsExcel method fires the excelExport event.

Added this function to the excelExport event. We are knockout / jQuery but the logic should work for you.
It simply goes through all the rows in the sheet and deletes cells based on our excelHidden property.
self.resultsExcelExport = function(e) {
var sheet = e.workbook.sheets[0];
for (var i = this.columns.length - 1; i >= 0; i--) {
if (this.columns[i].excelHidden) {
sheet.columns.splice(i, 1);
sheet.rows.forEach(function(row) {
row.cells.splice(i, 1);
})
}
}
}
In the grid columns object add a property "excelHidden: true" to signal which columns to hide.
{
field: "Id",
title: 'Download',
excelHidden: true,
},
UPDATE:
Well that worked until I tried grouping a column. So had to lookup via title to find the correct column number to remove. Now looks like this.
function(e) {
var hiddenColumnsByTitle = {};
var hiddenColumns = [];
var sheet = e.workbook.sheets[0];
this.columns.forEach(function(column) {
if (column.excelHidden) {
hiddenColumnsByTitle[column.title] = true;
}
})
for (var r = 0; r < sheet.rows.length; r++) {
var row = sheet.rows[r];
if (row.type === "header") {
row.cells.forEach(function(headerCell, index) {
var columnTitle = headerCell.value;
if (hiddenColumnsByTitle[columnTitle]) {
hiddenColumns.push(index);
}
})
break;
}
}
for (var i = hiddenColumns.length - 1; i >= 0; i--) {
var hiddenColumn = hiddenColumns[i];
sheet.columns.splice(hiddenColumn, 1);
sheet.rows.forEach(function(row) {
row.cells.splice(hiddenColumn, 1);
})
}
}

Related

React Hooks onkeydown update

I am having problems with the hooks in this code. For some reason, the maze updates only after changing the value in the input, when it should update the screen every time an arrow is pressed.
const [laberinto, setLab] = useState([])
const [wTemp, setWtemp] = useState(4)
const [hTemp, setHtemp] = useState(4)
let array = []
const Maze = () => {
fetch("https://maze.juanelcaballo.club/?type=json&w="+wTemp+"&h="+hTemp).then(response => {
return response.json();
}).then(responseInJSON => {
const lab = responseInJSON.map(obj1 => {
let objeto = "";
obj1.forEach(i => {
if (i == " ") {
objeto = objeto + " ";
} else {
objeto = objeto + i;
}
});
array.push(objeto)
});
});
setLab(array)
}
useEffect(() =>{
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === 'ArrowUp') {
setLab(Movements(laberinto, "UP"))
} else if (e.key === 'ArrowDown') {
setLab(Movements(laberinto, "DOWN"))
} else if (e.key === 'ArrowLeft') {
setLab(Movements(laberinto, "LEFT"))
} else if (e.key === 'ArrowRight') {
setLab(Movements(laberinto, "RIGHT"))
}
}
});
console.log(laberinto)
return(
<div className="App">
<div className="bottom">
<h4>WxH:</h4>
<input type="number" id="w" name="w" defaultValue={wTemp} min="2" max="10" onChange={e => setWtemp(e.target.value)} />
<input type="number" id="h" name="h" defaultValue={hTemp} min="2" max="10" onChange={e => setHtemp(e.target.value)} />
<button className="button" onClick={(Maze)}>"NEW GAME"</button>
</div>
<Page laberinto={laberinto}/>
</div>
)
here i click leftarrow 3 times but didn't update the screen until I change from 2 to 3 at the input box.

Trying to upload an image using laravel and vuejs

I have my upload html form like this
<div class="album_single_data settings_data">
<div class="album_single_img">
<figure class="thumbnail thumbnail-rounded">
<img class="main-profile" :src="getprofilepicture()" alt="">
</figure>
</div>
<div class="album_single_text">
<div class="album_btn profile_image">
<div class="btn btn-primary">
<input class="settings-file-upload" type="file" #change="updateProfilePic" ref="file" accept="image/*">
<div class="settings-file-icon">
<span class="material-icons">camera_alt</span>
Upload Profile Picture. (Note: Must Be Less Than 2MB)
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<button #click="updateInfo" class="btn btn-primary">Update Profile</button>
</div>
This is the vuejs code that handles the form submit/upload method which means anytime i click on the upload button the image uploads. but the problem is that it does not submit
export default {
name: 'Settings',
components: {
//
},
data() {
return{
form: new Form ({
id: '',
username:'',
email: '',
password: '',
name: '',
bio: '',
twitter_handle: '',
facebook_handle: '',
instagram_handle: '',
backgroundPicture: ''
}),
pic: ({
profilePicture: '',
})
}
},
created(){
axios.get("profile")
.then(({ data })=>(this.form.fill(data)))
.then(()=>(this.pic.data))
;
},
methods: {
getprofilepicture()
{
//default avatar pic if there is no photo of user
let profilePicture = "http://localhost:8000/img/profile/user.png";
//returns the current path of the
if (this.pic.profilePicture) {
if (this.pic.profilePicture.indexOf('base64') != -1) {
profilePicture = this.pic.profilePicture;
} else {
profilePicture = 'http://localhost:8000/img/profile/' + this.pic.profilePicture;
}
return profilePicture;
}
return profilePicture;
//let profilePicture = (this.form.profilePicture.length > 200) ? this.form.profilePicture : "http://localhost:8000/img/profile/"+ this.form.profilePicture ;
//return profilePicture;
},
// getBackgroundPic(){
// let backgroundPicture = "http://localhost:8000/img/profile/background.jpg";
// if(this.form.backgroundPicture){
// if(this.form.backgroundPicture.indexOf('base64') != -1){
// backgroundPicture = this.form.backgroundPicture;
// }else {
// backgroundPicture = 'http://localhost:8000/img/profile/'+ this.form.backgroundPicture;
// }
// return backgroundPicture;
// }
// return backgroundPicture;
// },
updateInfo(){
this.$Progress.start();
this.form.put('profile')
.then(()=> {
this.$Progress.finish();
this.$router.go('/profile')
})
.catch(()=>{
this.$Progress.fail()
})
},
updateProfilePic(e){
let file = e.target.files[0];
//console.log(file);
let reader = new FileReader();
if(file['size'] < 2097152){
reader.onloadend = () => {
//console.log('RESULT', reader.result)
this.pic.profilePicture = reader.result;
}
reader.readAsDataURL(file);
}else{
Toast.fire({
icon: 'error',
title: 'Ooops...',
text: 'The file you are trying to upload is more than 2MB',
})
}
},
updateBackgroundPic(e){
let file = e.target.files[0];
//console.log(file);
let reader = new FileReader();
if(file['size'] < 2097152){
reader.onloadend = () => {
this.form.backgroundPicture = reader.result;
}
reader.readAsDataURL(file);
}else{
Toast.fire({
icon: 'error',
title: 'Ooops...',
text: 'The file you are trying to upload is more than 2MB'
})
}
}
}
}
</script>
Anytime i click on the submit button i have this error message: "Undefined offset: 1", exception: "ErrorException",…}
exception: "ErrorException"
and i really do not know the cause of this error.
Below is the PHP Code that handles the server side part of the upload
public function updateProfile(Request $request)
{
$user = auth('api')->user();
$this->validate($request,[
'username' => 'required|string|max:255|unique:users,username,'.$user->id,
'name' => 'max:255',
'email' => 'required|string|email|max:255|unique:users,email,
'.$user->id,
'password' => 'sometimes|required|min:8'
]);
$currentProfilePicture = $user->profilePicture;
if($request->profilePicture != $currentProfilePicture){
$name = time().'.' . explode('/', explode(':', substr($request->profilePicture, 0, strpos($request->profilePicture, ';')))[1])[1];
Image::make($request->profilePicture)->save(public_path('img/profile/').$name);
$request->merge(['profilePicture' => $name]);
$userPhoto = public_path('img/profile/').$currentProfilePicture;
if(file_exists($userPhoto)){
#unlink($userPhoto);
}
}
$user->update($request->all());
}

How to prevent vote multiple/fast clicking in Vue exploit?

I have a vote system that allows the users to vote-up or unvote.
The issue is when a user clicks on the vote button multiple times quickly. It causes the number of upvotes to go up and down in a weird way (It goes up 1,2,3 and then down to below 0, probably has to do with the Vote.vue which adds and subtracts 1's from the total votes so when clicked fast enough it causes this weirdness) .
Also, I took care to have unique questions id and user id in the vote table.
I have these simple vote/unvote methods in the QuestionsController:
/**
* Vote a question up.
*/
public function voteUp(Question $question)
{
Auth::user()->votes()->attach($question->id);
}
/**
* Vote a question down.
*/
public function voteDown(Question $question)
{
Auth::user()->votes()->detach($question->id);
}
And maybe more importantly and probably causing the issue is the Vote.vue, as you may see I add/subtract 1 to the total #votes displayed.
methods: {
voteUp(question) {
axios.post('/voteup/'+question)
.then(response => this.isVoted = true, this.votes = this.votes + 1)
.catch(response => console.log(response.data));
},
voteDown(question) {
axios.post('/votedown/'+question)
.then(response => this.isVoted = false, this.votes = this.votes - 1)
.catch(response => console.log(response.data));
}
}
How do I overcome this so users won't exploit this + make it behave more stable?
EDIT: I tried these solutions but it didn't solve the issue:
#1 - I tried #Radu Diță solution:
<vote
:votes="{{ $question->votes()->count() }}"
:question="{{ $question->id }}"
:voted="{{ $question->currentUserVoted() ? 'true' : 'false' }}"
:disabled="voteInAir"
></vote>
and the Vote.vue script:
// resources/js/components/Vote.vue
<template>
<span>
<a href="#" v-if="isVoted" #click.prevent="voteDown(question)">
<i class="fas fa-caret-up fa-3x text-primary vote-effect vote-up-effect"></i>
</a>
<a href="#" v-else #click.prevent="voteUp(question)">
<i class="fas fa-caret-up fa-3x vote-gray vote-effect"></i>
</a>
<span class="vote-count-post "><strong>{{ this.votes }}</strong></span>
</span>
</template>
<script>
export default {
props: ['question', 'voted', 'votes'],
data: function() {
return {
isVoted: '',
voteInAir: false
}
},
mounted() {
this.isVoted = this.isVote ? true : false;
},
computed: {
isVote() {
return this.voted;
},
},
methods: {
voteUp(question) {
this.voteInAir = true;
axios.post('/voteup/'+question)
.then(response => this.isVoted = true, this.votes = this.votes + 1, this.voteInAir = false)
.catch(response => console.log(response.data), this.voteInAir = false);
},
voteDown(question) {
this.voteInAir = true;
axios.post('/votedown/'+question)
.then(response => this.isVoted = false, this.votes = this.votes - 1, this.voteInAir = false)
.catch(response => console.log(response.data), this.voteInAir = false);
}
}
}
</script>
#2 - I also tried #Rijosh solution:
<vote
:votes="{{ $question->votes()->count() }}"
:question="{{ $question->id }}"
:voted="{{ $question->currentUserVoted() ? 'true' : 'false' }}"
></vote>
And this is with time interval delay which didnt work:
<template>
<span>
<a href="#" v-if="isVoted" #click.prevent="voteDown(question)">
<i class="fas fa-caret-up fa-3x text-primary vote-effect vote-up-effect"></i>
</a>
<a href="#" v-else #click.prevent="voteUp(question)">
<i class="fas fa-caret-up fa-3x vote-gray vote-effect"></i>
</a>
<span class="vote-count-post "><strong>{{ this.votes }}</strong></span>
</span>
</template>
<script>
export default {
props: ['question', 'voted', 'votes'],
data: function() {
return {
isVoted: '',
timer: null,
interval: 200,
}
},
mounted() {
this.isVoted = this.isVote ? true : false;
},
computed: {
isVote() {
return this.voted;
},
},
methods: {
voteUp(question) {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
axios.post('/voteup/'+question)
.then(response => this.isVoted = true, this.votes = this.votes + 1)
.catch(response => console.log(response.data));
}, this.interval);
},
voteDown(question) {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
axios.post('/votedown/'+question)
.then(response => this.isVoted = false, this.votes = this.votes - 1)
.catch(response => console.log(response.data));
}, this.interval);
}
}
}
</script>
Since you are only allowing one vote (or that is what i understand) i would not increase the upvotes directly but make kind of a system with booleans kind of like this:
<button v-on:click="activateUpVote">UpVote</button>
<button v-on:click="activateDownVote">DownVote</button>
.
.
.
data(){
return{
upVote: false,
downVote: false,
hasVoted: false
}
},
methods: {
activateUpVote(){
this.upVote = !this.upVote
if(this.upVote && (!this.hasVoted || this.downVote)){
this.downVote = false
this.hasVoted = true
this.vote('/votedown/'+this.question, this.votes + 1)
}else{
this.hasVoted = false
}
},
//downVote same as above but the other way around except for the hasVoted part
vote(route, votes) {
axios.post(route)
.then(response => votes)
.catch(response => console.log(response.data));
},
}
Try this solution
Template
<div>
<button #click="voteUp">UpVote</button>
<button #click="voteDown">DownVote</button>
</div>
Data
data: () => {
return {
timer: null,
interval: 200
}
},
Methods
methods: {
voteUp: function () {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
console.log('up vote')
// your action
}, this.interval);
},
voteDown: function () {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
console.log('down vote')
// your action
}, this.interval);
}
}
I'm using setTimeout and clearTimeout in the methods. This will prevent consecutive actions on the buttons. Continuous clicks will trigger only one action. The time between each click can be controlled using the interval value in the data.
You should disable the button after it was pressed and enabled it again after you get a response. This is usable for any button that you wan't to disable spamming.
In your data add a boolean for each button
data () {
return {
//other data
upVoteInAir: false
}
}
then bind your button based on the flag
<button :disabled="upVoteInAir">Up</button>
update the flag based on the http request
methods: {
voteUp(question) {
this.upVoteInAir = true
axios.post('/voteup/'+question)
.then(response => this.isVoted = true, this.votes = this.votes + 1, this.upVoteInAir = false)
.catch(response => console.log(response.data), this.upVoteInAir = false);
},
}

How to fire Ajax function with radiobutton click MVC

I have a View made up of 3 Partial Views:
Partial View #1 is listing RadioButtons. When I click a button I would like to send the resulting selection to the controller in order to update the Partial View #2 (which is a Telerik grid). Then the selection in PV #2 will update Partial View #3 (another Telerik grid).
I am currently doing a Window.location to send selected values to the Controller which causes the Controller and ViewModel to be reloaded and lose state. Other than losing the state, the flow works as I would like.
Is there a way to do this in Ajax to where I do not have to reload the Controller?
I will spare you the Partial Views #2 & #3 of the Telerik grids. If I can get the answer to how to get the radio button to call the Controller without a postback, I can apply the same to those Partial Views.
View:
#model MyProject.ViewModel.MaterialsListVM
#{
ViewBag.Title = "Materials List";
}
<div>
<div class="row">
<div class="col-sm-2">
#Html.Partial("MaterialTypeFilter")
</div>
<div class="col-lg-10">
#Html.Partial("MaterialsGrid")
</div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-sm-2"> </div>
<div class="col-lg-10">
#Html.Partial("ProjectMaterialsGrid")
</div>
</div>
</div>
Partial View #1:
#model MyProject.ViewModel.MaterialsListVM
<div class="container">
<h5> #Html.Label("Material Type: ", new { style = "font-weight:bold;" })</h5>
#if (Model != null)
{
for (int i = 0; i < Model.Material_Type.MaterialTypeList.Count; i++)
{
<span class="btn-group-sm">
#Html.RadioButton("MatType", new { Model.Material_Type.MaterialTypeList[i].Name }, Model.Material_Type.MaterialTypeList[i].Selected, new { #onclick = "CallChangefunc(this.value)" })
#Html.Label(Model.Material_Type.MaterialTypeList[i].Name)
</span><br />
}
}
</div>
<script>
function CallChangefunc(value) {
var selected = value.replace("{ Name = ", "");
selected = selected.replace("}", "");
window.location = '#Url.Action("Index", "MaterialsList")' + '?selectedMatType=' + selected;
//alert("val is:" + selected);
}
Update
I changed the JavaScript to this Ajax call and that called my Controller HttpPost Index() function:
$(document).ready(function () {
$(':radio[name="MatType"]').change(function (e) {
$.ajax({
type: 'POST',
url: '/MaterialsList/Index',
data: { selectedMatType: $(':radio[name="MatType"]:checked').val()},
dataType: "json",
success: function (data) {
alert('did it');
}
});
});
})
Unfortunately the Controller is still being reloaded and I'm losing the state of the data selected into the Partial View #3 Telerik grid...
...any help would be much appreciated...
Update #2
This solution sucks but I don't know what else to do...
The goal is to retain the state of the datasource for a Telerik grid in Partial View #3. The datasource is a ViewModel with a List of class objects.
Added Partial View #2 code and ActionResult from Controller. There is no HttpPost Index function. Every radio button change fires the ActionResult Index. Every select event in Telerik grid fires ActionResult Index.
Pertinent Controller code:
public ActionResult Index(string selectedMatType, string selectedMaterials, string projectMaterialsList)
{
if (projectMaterialsList != null)
{
materialsListVM.ProjectMaterialsList = JsonConvert.DeserializeObject<List<ProjectMaterialsListVM.ProjectMaterial>>(projectMaterialsList);
}
if (selectedMatType != null)
{
materialsListVM.SelectMaterialType(selectedMatType);
materialsListVM.GetMaterials();
}
if (selectedMaterials != null)
{
string[] materialIds = selectedMaterials.Split(',');
foreach (string id in materialIds)
{
MoveToProjectMaterialsList(id, selectedMatType);
}
}
ViewBag.ProjectMaterialsList = materialsListVM.ProjectMaterialsList;
ViewBag.SelectedMatType = selectedMatType;
return View(materialsListVM);
}
Partial View #2 code:
#model MyProject.ViewModel.MaterialsListVM
<div>
#if (ViewBag.SelectedMatType == "Cap Weld")
{
#(Html.Kendo().Grid(Model.CapWeld_Materials)
.Name("grid")
.Columns(columns =>
{
columns.Select().Width(40);
columns.Bound(c => c.MaterialId).Hidden();
columns.Bound(c => c.MaterialTypeName);
columns.Bound(c => c.PcsPartNum);
columns.Bound(c => c.ClientPartNum);
columns.Bound(c => c.Type).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.OuterDiameter).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.WallThickness).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.Specification).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.Grade).Filterable(ftb => ftb.Multi(true));
})
.Events(ev => ev.Change("onChange"))
.Pageable()
.Sortable()
.Scrollable()
.TableHtmlAttributes(new { width = "100%" })
//.HtmlAttributes(new { style="height:500px"})
.PersistSelection(true)
.Filterable()
.DataSource(datasource => datasource
.Ajax()
.ServerOperation(false)
.Model(m => m.Id(d => d.MaterialId))
)
);
}
</div>
#{
var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var val = jss.Serialize(ViewBag.ProjectMaterialsList);
}
<script>
function onChange(arg) {
var selectedMatType = '#(ViewBag.SelectedMatType)';
var projectMaterialsList = '#Html.Raw(val)';
//var obj = $.parseJSON(val);
var grid = $('#grid').data('kendoGrid');
var selectedMaterials = grid.selectedKeyNames().join(", ");
// alert(selectedMaterials);
#*$.ajax({
type: "POST",
data: { selectedMatType: selectedMatType, selectedMaterials: selectedMaterials, projectMaterialsList: projectMaterialsList },
dataType: "json",
url: #Url.Action("Index", "MaterialsList")
});*#
window.location = '#Url.Action("Index", "MaterialsList")' + '?selectedMatType=' + selectedMatType + '&selectedMaterials=' + selectedMaterials
+ '&projectMaterialsList=' + projectMaterialsList;
}
</script>

Kendo Grid doesn't send all checked items

I have a Kendo Grid that has checked box items. I want to check all items and send them to the controller, for export checked data to PDF. But, when I have checked all items, Kendo Grid sends only checked items from the first page of grid, and my report in PDF has only one page. How to get all checked items from Kendo Grid? My code is here:
<div style="text-align:right; font-size: 0.9em;height:28px;position: relative;">
<span style="float:left;text-align:left;">
Check All
Uncheck All
<a class="k-button k-button-icontext k-grid-Patient" id="hrefCheckedPatients" href="#" onclick="getChecked();">Export to PDF</a>
Download Generated PDF
<label id="checkedMsg" style="color:red;display:none;"></label>
</span>
(Html.Kendo().Grid<RunSummary>()
.Name("CheckedPatients")
.DataSource(datasource => datasource
.Ajax().PageSize(25)
.Sort(sort => sort.Add("UniqueId").Ascending())
.Read(read => read.Action("GetRunSummaries", "PatientReport")))
.Columns(columns =>
{
columns.Bound(c => c.UniqueId).Title(ELSORegistry.Resources.Views.Home.HomeStrings.UniqueId)
.ClientTemplate("<input type='checkbox' class='primaryBox' id='#= UniqueId #'>#= UniqueId #</input>");
columns.Bound(c => c.RunNo).Title(SharedStrings.Run);
columns.Bound(c => c.Birthdate).Title(SharedStrings.Birthdate).Format("{0:g}").Filterable(true);
columns.Bound(c => c.customAge).Title(SharedStrings.Age)
.Filterable(
filterable => filterable
.UI("AgeFilter")
.Extra(false)
.Operators(operators => operators
.ForString(str => str.Clear().IsEqualTo("Is equal to"))
)
);
columns.Bound(c => c.TimeOn).Title(PatientStrings.DateOn)
.Format("{0:g}")
.Filterable(true);
columns.Bound(c => c.TimeOff).Title(PatientStrings.DateOff)
.Format("{0:g}")
.Filterable(true);
columns.Bound(c => c.DischargedAlive).Title(PatientStrings.DischargedAlive).Filterable(true).ClientTemplate("#= DischargedAlive ? 'Yes' : 'No' #");
columns.Bound(c => c.ShowSubmitted).Title(PatientStrings.Submitted).Filterable(true).ClientTemplate("#= ShowSubmitted ? 'Yes' : 'No' #");
columns.Bound(c => c.SupportTypeEnum).Title(PatientStrings.SupportType).Filterable(true);//.ClientTemplate("#= SupportType ? 'Yes' : 'No' #");
}
)
.Pageable(p => p.PageSizes(new[] {10, 25, 50, 100}))
.Sortable()
.Filterable( )
.Events( e => e.FilterMenuInit("FilterMenuFuncWithAge") ) // apply x [closing box] on pop up filter box
)
function getChecked() {
$('#checkedMsg').text('');
var ids = new Array();
$('.primaryBox:checked').map(function (index) {
ids.push(this.id);
});
if (ids.length == 0) {
$('#checkedMsg').text('#ELSORegistry.Resources.Views.Patient.PatientStrings.NoPatientsSelected').show();
$('#hrefCheckedPatients').blur();
return;
} else {
$('#checkedMsg').text('').hide();
$('#hrefCheckedPatients').blur();
}
$.ajax({
type: "POST",
url: "/PatientReport/ExportToPDF",
dataType: "json",
traditional: true,
data: { uniqueIds: ids },
success: function (data) {
if (data.success) {
$('#lnkPdfDownload').show();
$('#lnkPdfDownload').attr('href', '/PatientReport/DownloadFile' + '?fName=' + data.fName);
} else {
$('#lnkPdfDownload').hide();
}
},
error: function (jqXHR, textStatus, errorThrown) {
$('#checkedMsg').text('#ELSORegistry.Resources.Views.Patient.PatientStrings.CheckedError').show();
$('#hrefCheckedPatients').blur();
}
});
}
</script>
Thank you in advance for any help.
We can't get directly all Ids from the kendo grid from all pages. So we have to manually get it from all the pages.
Please try with the below code snippet.
<script>
var ids = [];
function checkAll() {
var dataSource = $("#CheckedPatients").data("kendoGrid").dataSource;
var filters = dataSource.filter();
var totalRowCount = parseInt(dataSource.total().toString());
var totalPages = Math.ceil(totalRowCount / dataSource.pageSize());
var currentPageSize = $("#CheckedPatients").data("kendoGrid").dataSource.page();
PageTraverser(dataSource, 1, totalPages,filters, function () {
$("#CheckedPatients").data("kendoGrid").dataSource.page(currentPageSize);
alert("You have slected this Ids:- " + ids.join(','));
});
}
function PageTraverser(dataSource, targetPage, totalPages,filters, completionFunction) {
dataSource.query({
page: targetPage,
filter: filters
pageSize: 1,
}).then(function () {
var view = dataSource.view();
for (var viewItemId = 0; viewItemId < view.length; viewItemId++) {
var viewItem = view[viewItemId];
ids.push(viewItem.UniqueId); //Please change your field name here
}
targetPage++;
if (targetPage <= totalPages) {
PageTraverser(dataSource, targetPage, totalPages, filters, completionFunction);
} else {
completionFunction();
}
});
}
</script>
Let me know if any concern.

Resources