I want to sort the table by name and first name and if we find duplicate fields we leave one line by adding an accordion to show duplicates or masked.
I put the ts and html code
i have create this sample for you, i have remove Observable to simplify the code :
First i have create new structure with some meta data information.
isFirst and isExpended are useful on html side to manage "hide/show" state of related Clients.
export interface OrderedClient {
isUniq: boolean;
isFirst: boolean;
isExpended: boolean;
info: Client;
}
I have put dummy data on constructor to show you how to classify this:
export class AppComponent {
private clients: Client[];
displayClients: OrderedClient[];
constructor() {
this.clients = [
{id: 1, lastname: 'git',firstName: 'yanis', birthDate:'03/19/1990'},
{id: 2, lastname: 'git',firstName: 'yanis', birthDate:'01/01/1990'},
{id: 3, lastname: 'lorem',firstName: 'yanis', birthDate:'01/01/1990'}
];
this.classify();
}
private classify() {
this.displayClients = [];
// For each existing clients.
this.clients.forEach((client: Client) => {
// We create new structure describe by OrderedClient interface.
this.displayClients.push(
// We merge client and getInformation output.
Object.assign({
info: client
},this.getInformation(client))
);
});
}
private getInformation(client: Client): {
isFirst: boolean,
isUniq: boolean,
isExpended: boolean
} {
// We fetch all clients who have same first and last name.
const clients = this.clients.filter(current => {
return current.lastname === client.lastname && current.firstName === client.firstName;
});
// We create meta information
return {
// If is first item of list.
isFirst: clients[0].id === client.id,
// if he is alone.
isUniq: clients.length === 1,
// If he is visible by default.
isExpended: clients.length === 1 || clients[0].id === client.id
};
}
toggleRelated(client:Client)
{
//When we click on toggle button. we loop on all clients.
this.displayClients.forEach(e => {
// If current one have same first and last name and is not the first element of associated clients.
if(e.info.firstName === client.firstName && e.info.lastname === client.lastname && !e.isFirst) {
// Then we toggle expended state.
e.isExpended = !e.isExpended;
}
});
}
}
for each clients, i will check if we can find similar other entry on document. Goal is to populate isFirst and isExpended. Now we can use this new structure on our html as bellow :
<div class="m-table" data-module="scrollable">
<table>
<thead>
<tr>
<th>#</th>
<th sortable-column="lastname" scope="col">Nomtrie</th>
<th sortable-column="firstname" scope="col">Prénomtrie</th>
<th scope="col">Date de naissancetrie</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let data of displayClients">
<tr *ngIf="data.isExpended">
<td><button *ngIf="data.isFirst && !data.isUniq" (click)="toggleRelated(data.info)">toggle</button></td>
<td scope="col">{{data.info.lastname}}</td>
<td scope="col">{{data.info.firstName}}</td>
<td scope="col">{{data.info.birthDate}}</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
online sample
You can "map" your result, adding a property to check if is repeated, but first of all you must ordered the array
this.searchService.getAllClients()
.subscribe(results =>{
//first sort
results.sort((a, b) => {
return a.lastname + a.firstname == b.lastname + b.firstname ? 0 :
a.lastname + a.firstname > b.lastname + b.firstname ? 1 : -1;
//After, map the result adding a new propertie
this.results=result.map((x) => {
return {...x, uniq: results.find(
f => f.lastname + f.firstname == x.lastname + x.firstname) == x }
})
),
error =>console.log('results', error );
Notice, I use this.result.find that return an element. if this element is equal so self, is the first one of them
Related
So I'm making a React program that's about sports. In there I have a table that shows user inputs and those are saved in Firebase realtime database. I'm getting the data from the database and showing it in the table, but I have to make some calculations with those inputs. If the user for example hasn't filled some fields, the end "Total score" will show as NaN because in my calculation the first thing will b 0-x and that makes it NaN.
table example
I have tried making a function for calculating the points outside or the return and then I possibly could call it out but Im not really sure how to access the data cause the database thing kinda makes it difficult for me. My code snippets are down here
Separate calculation.
const hundredPoints = () => {
var number = 0;
let result = decathletes.map(a => a.hundredMeters);
if(result === '')
{number += 0
} else if(result < 9.5 || result > 20) {
number += 0;
} else {
number += Math.floor(hundred.hundredMeters.a*Math.pow((hundred.hundredMeters.b-Number(result)), hundred.hundredMeters.c))
}
console.log(number);
return number;
}
My array where I add them database object locally.
const [decathletes, setDecathletes] = useState([])
useEffect(() => {
getDecathletes()
}, [])
function getDecathletes() {
const decathletesCollectionRef = collection(db, 'decaTable');
getDocs(decathletesCollectionRef)
.then(response => {
const deca = response.docs.map(doc => ({ data: doc.data(), id: doc.id }))
setDecathletes(deca)
console.log(q)
})
.catch(e => console.log(e.message));
}
The Table in the return statement
< Table>
<Fragment>
<div>
<thead>
<tr>
<th>Name</th>
<th>Date of birth</th>
<th>100m</th>
<th>Long</th>
<th>Shot Put</th>
<th>Total score</th>
</tr>
</thead>
<tbody>
{decathletes.map((decas) => (<tr>
<td key={decas.id}>{decas.data.name}</td>
<td key={decas.id}>{decas.data.dateOfBirth}</td>
<td key={decas.id}>{decas.data.hundredMeters}</td>
<td key={decas.id}>{decas.data.longJump}</td>
<td key={decas.id}>{decas.data.shotPut}</td>
<td key={decas.id}>{Number(Math.floor(hundred.hundredMeters.a * Math.pow((hundred.hundredMeters.b - Number(decas.data.hundredMeters)), hundred.hundredMeters.c))) +
Number(Math.floor(long.longJump.a * Math.pow(Number(decas.data.longJump) * 100 - long.longJump.b, long.longJump.c))) +
Number(Math.floor(shot.shotPut.a * Math.pow((Number(decas.data.shotPut - shot.shotPut.b)), shot.shotPut.c)))}</td>
</tr>))}
</tbody>
</div>
</Fragment>
</Table>
If anyone can help, I'd appreciate it immensely, my own brain cant figure it out anymore although it might be really obvious. :D
ISSUE
Hello guys, can anyone please help me to solve this complicated issue? :
I'm creating an application using Spring boot v2.0.5 and React.js v15.6.2, ReactDom v15.6.2, React Bootstrap v0.32.4, and the linking between my frontend and serverside parts is made of restful web services using Spring annotations on the back and fetch API on the front. My React components are made by following the Parent-Children design pattern concept, that means: some of my components can be children of some others and vice versa.
How it works?
I have a table with columns and rows, each row inside the table has a unique id, 2 drop-downs, 1 text input, 2 datepickers and 1 file upload input which is causing the main issue; The user can add more rows that has same components as the previous ones, by clicking on the "+ Document" button; Each row has a unique incremental Id of type number (integer); the drop-downs and the inputs events are handled by one method inside the parent component based on their tag names;
I'm storing all data entered by the user inside a list ([]) of objects({}).
Example: if the user fill only the first row; the object stored inside the list state will be like this:
[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)}]
if the user adds one other row and then filled it like the first one, the list will be like this:
[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)},{id:1,type:"howTo",lang:"en",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"01-01-2019",document:{File(742015)}]
Check this image to see how the table look like: Table Demo
The table as code in Presentational component Class (child of the main component)
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
docObjList: [],
element: (
<FormDocRowItem // this contains the table tbody tds elements..
id={1}
handleChanges={this.props.handleChanges}/>)
};
this.handleAddDocumentRow = this.handleAddDocumentRow.bind(this);
}
// handleAddDocumentRow method
handleAddDocumentRow(e) {
const value = e.target.value;
const name = e.target.name;
if (name === 'add') {
let arr = this.state.docObjList; // get the list state
// assign the new row component
arr = [...arr, Object.assign({}, this.state.element)];
// set the new list state
this.setState({docObjList: arr});
}
// if name === 'delete' logic..
}
// render method
render() {
const {handleReset} = this.props;
return(
<FormGroup>
<Form encType="multipart/form-data">
<Table striped bordered condensed hover>
<thead>
<tr>
<th>id</th>
<th>Type</th>
<th>Lang</th>
<th>Title</th>
<th>Date begin</th>
<th>Date end</th>
<th>+ Document</th>
<th>Options</th>
</tr>
</thead>
<tbody>
{this.state.element} // this row is required as initialization
{
this.state.docObjList.map((doc, index) => {
// as index in map() starts from 0 and there is an
// already row component above => The index inside the
// table should start from 1 except The key property
// which should know the right index of the function
const id = index+1;
return (
<tr key={index}>
<td>
{id}
</td>
<td>
<DocumentTypes id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateBegin" componentClass="input" type="date"
onChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateEnd" componentClass="input" type="date"
onChange={this.props.handleChanges}/>
</td>
<td>
<Document id={id} handleChange={this.props.handleChanges}/>
</td>
{
this.state.docObjList.length == index + 1 &&
<td>
<button type="button" style={{verticalAlign: 'middle', textAlign: 'center'}} id={index + 1}
name="delete"
onClick={this.handleAddDocumentRow}>
Delete
</button>
</td>
}
</tr>
);
})
}
</tbody>
</Table>
<button type="button" name="add" onClick={this.handleAddDocumentRow}>+ Document</button>
<FormGroup>
<Button type="reset"
style={{marginRight: '20%'}}
className="btn-primary"
onClick={this.props.handleClickSubmit}>Submit</Button>
<Button name="back" onClick={this.props.handleClickSubmit}>Annuler</Button>
</FormGroup>
</Form>
</FormGroup>
)
}
}
The row component class (Child component of Presentational)
const FormDocRowItem = (props) => {
const {id} = props; // the ID here is refering the column that is going to be
// show inside the table not the index of the map function
return(
return (
<tr>
<td>
{id}
</td>
<td>
<DocumentTypes id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateBegin" componentClass="input" type="date" onChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateEnd" componentClass="input" type="date" onChange={this.props.handleChanges}/>
</td>
<td>
<Document id={id} handleChange={this.props.handleChanges}/>
</td>
</tr>
);
}
}
Parent Component Class (The main component)
constructor(props) {
this.state ={
docDataList: [],
formIsReadyToSubmit: false
}
this.handleSubmit = this.handleSubmit.bind(this); // button submit click
this.handleReset = this.handleReset.bind(this); // button reset click
this.fillWithData = this.fillWithData.bind(this); // handle changes
}
// handleReset method..
fillWithData(e) {
const name = e.target.name; // get the name of the target
const id = parseInt(e.target.id); // get the id of the target
let value = e.target.value; // get the value of the target
let arr = this.state.docDataList; // get the list state
// if the target is a file upload
if (name === 'selectDocument')
value = e.target.files[0];
// create properties with null values starting from the first onchange
// event handling, to not get a misplaced properties inside the
// objects of the list state
arr.map((x) => {
x.type = x.type ? x.type : null;
x.lang = x.lang ? x.lang : null;
x.libelle = x.libelle ? x.libelle : null;
x.dateBegin = x.dateBegin ? x.dateBegin : null;
x.dateEnd = x.dateEnd ? x.dateEnd : null;
x.document = x.document ? x.document : null;
});
// if the event target name is not delete
if (name != 'delete') {
// check if the object id already exist in the table
// if it exists, the new value should replace the previous one
// and not allowed to add a new object to the list state
if ((arr.find((x) => x.id == id))) {
// loop through the list state to find the id of the object
arr.map((x) => {
if (x.id == id) {
// helper variable to prevent empty strings
const val = value != '' ? value : null;
switch (name) {
case 'selectType':
x.type = val;
break;
case 'selectLang':
x.lang = val;
break;
case 'libelle':
x.libelle = val;
break;
case 'dateBegin':
x.dateBegin = val;
break;
case 'dateEnd':
x.dateEnd = val;
break;
case 'selectDocument':
x.document = val;
break;
}
}
});
// assign the new list to my docDataList state
// mentioning that the id of the element already exist
this.setState({docDataList: arr}, () => {
console.log(' ID exist; new dataList :', this.state.docDataList);
});
}
// if the id doesn't exist (means that the button +document is clicked)
else {
// again, a helper variable as the previous statement
const val = value != '' ? value : null;
this.setState({
docDataList: [...arr, Object.assign({
id: id,
type: name === 'selectType' ? val : null,
lang: name === 'selectLang' ? val : null,
libelle: name === 'libelle' ? val : null,
dateBegin: name === 'dataBegin' ? val : null,
dateEnd: name === 'dateEnd' ? val : null//,
//document: name==='selectDocument'? val:null
})]
}, () => {
console.log('ID doesnt exist; new dataList :', this.state.docDataList);
});
}
}
}
HandleSubmit() method (Inside the Parent component class)
// Submit button click handler
handleSubmit(e) {
let docDataList = this.state.docDataList;
// if the user didn't touch any thing on the table rows
// that means the list is empty and its length = 0
if (docDataList.length === 0) {
this.setState({
alerts: {
message: 'Please enter your document information ',
show: true
}
});
}
// if the user has entered a data on the table row
else if (docDataList.length > 0) {
let data = new FormData(); // object which will be sent
// check the docDataList before request
console.log('DocDataList before request:', docDataList);
data.append('docDataList', JSON.stringify(docDataList));
fetch('http://localhost:8080/api/files/uploadFile', {
method: 'POST',
body: data
}).then(response => {
console.log('success document upload', response);
}).catch(error => {
console.log('error', error);
});
this.setState({
formIsReadyToSubmit: true,
docDataList: [], // reset the list
alerts: {updateAlert: true} // make an alert
});
}
}
To see what the console show when I fill the row with data: CLICK HERE PLEASE
To see the response of the request: CLICK HERE PLEASE
NOTE: You may notice after watching those screenshots, that there is an extra list of data called "arrContrats" which which I didn't mention it in my issue because it doesn't have any problem; the problem is with the "docDataList" list. Thanks in advance
If your problem is that you're getting a File object from the browser, and then later using JSON.stringify on it (or something containing it) and getting {} for it in the JSON, that's correct. The browser's File object has no own, enumerable properties. JSON.stringify only includes own, enumerable properties.
If you want the various properties that File objects have (inherited accessor properties), you'll need to copy them to a new object.
If you want the file data, it's not accessible as a property on the object, you have to use one of the methods it inherits from Blob to read the file data, such as stream, text, or arrayBuffer (or alternatively, you could use a FileReader, but there's no need to except in obsolete environments that don't have the modern methods).
function stringifyFileObject(arrWithFiles = []) {
const arrData = [];
for (let i=0; i < arrWithFiles.length; i++) {
const file = arrWithFiles[i];
const obj = {
lastModified: file.lastModified,
name: file.name,
size: file.size,
type: file.type,
webkitRelativePath: file.webkitRelativePath,
}
arrData.push( obj );
}
}
Or whatever that suits your needs. You get the idea...
Don't use JSON.stringify() with FormData instance. And don't use headers: {'Content-Type': 'application/json'} if you are sending a file.
Example:
let data = new FormData(form);
let config = {
method: "POST",
body: data
}
const response = await fetch(url, config);
const responseData = response.json();
I am stuck with an issue in angular4 and node.js app. I display the data in a table that has 7 columns via *ngFor . Now the issue is , i need to dynamically and on real-time basis update the last column . i.e. if the column -> Active ... is Yes , then i display a green color and when the Active ...is No, then i display a red color. But then i am not sure how to update the last column and only the last column real-time.
[1]
I thought of using Observables code in init from where i call the table data, but that will only keep on showing the loader always , as the data will keep on uploading regularly after a few seconds and disturb the entire table view.
[2]
And i have shown the color based on an if condition, but in all the entries, it shows only green color.
code -
interface.component.ts
export class MasterInterfaceComponent implements OnInit {
activeCheck;
activeValue;
activeRedColor;
activeGreenColor;
ngOnInit() {
console.log("beginning of func");
Observable.interval(15000).subscribe(x => {
console.log("Called")
this.viewData();
});
}
viewData() {
this.loading = true;
var url = config.url;
var port = config.port;
this.http.post("http://" + url + ":" ...... ('Token') }) })
.map(result => this.result = result.json(),
)
.subscribe((res: Response) => {
this.loading = false;
this.records = res;
console.log("xxxx interface view result data ",this.result)
console.log("XXXXXXXXXXXX interface view res data ", res);
this.activeCheck = this.result;
for (let obj of this.activeCheck){
for (let key in obj){
if (key == "ACTIVE_YN"){
if (obj[key] == "Y"){
this.activeRedColor = false;
this.activeGreenColor = true;
console.log("this.activeGreenColor = true;");
}
else if (obj[key] == "N"){
this.activeRedColor = true;
this.activeGreenColor = false;
console.log("this.activeGreenColor = false;");
}
}
}
}
});
}
interface.component.html
<tbody>
<tr *ngFor="let data of result |filter:filter| orderBy : 'IF_ID'|
paginate: { itemsPerPage: 5, currentPage: p }; let i =
index">
<td class="text-center" style="width:8%">
<a [hidden]= "accessIdHide" [routerLink]="['/master-system/update-
interface']" (click)="forUpdate(data)" data-toggle="tooltip"
title="Update" style="color:#ffffff;;float:left"
type="link">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>{{data.IF_ID}}
</td>
<td>{{data.IF_CD}}</td>
<td>{{data.IF_NAME}}</td>
<td>{{data.IF_DESCRIPTION}}</td>
<td>{{data.SRC_SYS_NAME}}</td>
<td>{{data.TRGT_SYS_NAME}}</td>
<td >
<img [hidden]= "activeGreenColor" src = "url" width="10" height =
"10">{{data.ACTIVE_YN}}
<img [hidden] = "activeRedColor" src = "url" width="10" height =
"10">{{data.ACTIVE_YN}}
</td>
</tr>
</tbody>
First of all, you do not need two switches, but the way you had it is ok, but for our discussion, let's just say we have one switch "isActive".
I think your code should work, but you need to make sure the subscription is firing, ex.
.subscribe((res: Response) => {
console.log(res);
If this code fires every time the data changes, then your img should be turned on and off
<img *ngIf="isActive" src="url">
or
<img [ngClass]="{ isActive: green }">
Either of the above is fine. So just try to debug your observable to work the way you wanted, ex. when it should be fired and in which frequency.
I am wondering if there is a way to display for example an id in a table cell as another name from another object as their id's correspond to each other.
What i want to do is to display it in a way like this if there is something similar?
<td class="tableRowText"><p>{{l.SenderId}}</p></td>
in a way like this.
ng-options="x.ProcessId as x.Name for x in PL"
so that would be something like:
<td class="tableRowText"><p>{{l.SenderId}} as x.Name for x in PL</p></td>
WISHFULLY! :P hope you guys understand i'm just trying to get my point through.
Thanks in advance!
_____________EDIT:_______________________________
So this is the Table and how i request the data.
app.factory('getTableGridDataService', function ($resource, config) {
return $resource(config.apiURL + '/Logs/GetLogEvents', {}, { 'post': { method: 'POST' } })
});
scope.loggItems = [];
$scope.fillRealTable = function () {
var arrayBody = {
Sending: $scope.paramSending,
Receiving: $scope.paramReceiving,
Logging: $scope.paramLogging,
};
var query = postTableGridDataService.post({}, arrayBody);
query.$promise.then(function (data) {
var loggItemList = data;
$scope.loggItems = loggItemList
})
}
<table class="table table-striped table-condensed table-hover" id="MainTable">
<thead>
<tr>
<th ng-click="sort('SenderId')" style="cursor:pointer;">
Sender
<span class="glyphicon glyphicon-sort" ng-show="sortKey=='SenderId'" ng-class="{'glyphicon glyphicon-menu-up':reverse, 'glyphicon glyphicon-menu-down':!reverse}"></span>
</th>
<th ng-click="sort('ReceiverId')" style="cursor:pointer;">
Reciever
<span class="glyphicon glyphicon-sort" ng-show="sortKey=='ReceiverId'" ng-class="{'glyphicon glyphicon-menu-up':reverse, 'glyphicon glyphicon-menu-down':!reverse}"></span>
</th>
<th ng-click="sort('LoggingId')" style="cursor:pointer;">
Logging source
<span class="glyphicon glyphicon-sort" ng-show="sortKey=='LoggingId'" ng-class="{'glyphicon glyphicon-menu-up':reverse, 'glyphicon glyphicon-menu-down':!reverse}"></span>
</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="l in loggItems.LogEventList|filter:search|orderBy:sortKey:reverse|itemsPerPage:15" pagination-id="mainPagination">
<td class="tableRowText"><p>{{l.SenderId}}</p></td>
<td class="tableRowText"><p>{{l.ReceiverId}}</p></td>
<td class="tableRowText"><p>{{l.LoggingId}}</p></td>
</tr>
</tbody>
</table>
I also get another object from my api in which i have the names for the Sending-ID, Reciever-ID and Logging-ID and i want those names displayed in the table instead of the id's from my Object in wich i display in the tabl. how do i achieve that?
The other object with the corresponding ID's and names:
app.factory('getSearchFormData', function ($resource, config) {
return $resource(config.apiURL + '/SearchFormObj/getSearchFormItems', {} ,{'get': {method:'GET'}})
});
$scope.SL = [];
function SearchData() {
var query = getSearchFormData.get();
query.$promise.then(function (data) {
$scope.SL = data.SystemList;
console.log($scope.SL);
});
};
SearchData()
Here is the object returned from getSearchFormData:
The basic idea is in your controller which depends on both services, you wait for both promises to resolve, then loop through your log items and either replace the ids with the names, or add then names to each log item, if you want to keep the ids.
$scope.fillRealTable = function () {
var arrayBody = {
Sending: $scope.paramSending,
Receiving: $scope.paramReceiving,
Logging: $scope.paramLogging,
};
var p1 = postTableGridDataService.post({}, arrayBody).$promise;
var p2 = getSearchFormData.get().$promise;
//it might be $q.all([p1.promise,p2.promise])
$q.all([p1,p2]).then(function(data1, data2) {
var loggItemList = data1;
var SL = data2.SystemList;
loggItemList.forEach(function (item) {
item.SenderName = SL[item.SenderId].name;
item.ReceiverName = SL[item.ReceiverId].name;
item.LogginName = SL[item.LogginId].name;
});
$scope.loggItems = loggItemList;
});
}
The code in the forEach is all speculation, since you do not provide the structure of the data returned by getSearchFormData, so this example assumes it's key-value list of objects with the key being one of the ID. E.g:
{
"A001" : { name: "John Smith" },
"B001" : { name: "Bruce Wayne" }
}
P.S: An alternative to this method is to move the whole mapping in getTableGridDataService and have that service call the other, meaning your view logic remains simple, since it will receive the full result from the service.
Edited with working code as per Mark's answer below.
I'm really starting to loath MVC. I have been trying all day to get a simple grid to work, but I'm having more luck banging a hole in my desk with my head.
I'm trying to implement a search page that displays the results in a grid. There are 3 drop-down lists that the user can use to select search criteria. They must select at least one.
After they have searched, the user will be able to select which records they want to export. So I will need to include checkboxes in the resulting grid. That's a future headache.
Using JqGrid and Search form - ASP.NET MVC as a reference I have been able to get the grid to appear on the page (a major achievement). But I can't get the data to populate.
BTW, jqGrid 4.4.4 - jQuery Grid
here is my view:
#model Models.ExportDatex
<script type="text/javascript">
$(document).ready(function () {
$('#btnSearch').click(function (e) {
var selectedSchool = $('#ddlSchool').children('option').filter(':selected').text();
var selectedStudent = $('#ddlStudent').children('option').filter(':selected').text();
var selectedYear = $('#ddlYear').children('option').filter(':selected').text();
var selectedOption = $('#exportOption_1').is(':checked');
if (selectedSchool == '' && selectedStudent == '' && selectedYear == '') {
alert('Please specify your export criteria.');
return false;
}
selectedSchool = (selectedSchool == '') ? ' ' : selectedSchool;
selectedStudent = (selectedStudent == '') ? ' ' : selectedStudent;
selectedYear = (selectedYear == '') ? ' ' : selectedYear;
var extraQueryParameters = {
school: selectedSchool,
student: selectedStudent,
year: selectedYear,
option: selectedOption
};
$('#searchResults').jqGrid({
datatype: 'json',
viewrecords: true,
url: '#Url.Action("GridData")?' + $.param(extraQueryParameters),
pager: '#searchResultPager',
colNames: ['SchoolID', 'Student Name', 'Student ID', 'Apprenticeship', 'Result'],
colModel: [
{ name: 'SchoolID' },
{ name: 'Student Name' },
{ name: 'StudentID' },
{ name: 'Apprenticeship' },
{ name: 'Result' }]
}).trigger('reloadGrid', [{ page: 1 }]);
});
});
</script>
#using (Html.BeginForm("Index", "Datex", FormMethod.Post))
{
<h2>Export to Datex</h2>
<div class="exportOption">
<span>
#Html.RadioButtonFor(model => model.ExportOption, "true", new { id = "exportOption_1" })
<label for="exportOption_1">VET Results</label>
</span>
<span>
#Html.RadioButtonFor(model => model.ExportOption, "false", new { id = "exportOption_0" })
<label for="exportOption_0">VET Qualifications</label>
</span>
</div>
<div class="exportSelectionCriteria">
<p>Please specify the criteria you want to export data for:</p>
<table>
<tr>
<td>School:</td>
<td>#Html.DropDownListFor(model => model.SchoolID, Model.Schools, new { id = "ddlSchool" })</td>
</tr>
<tr>
<td>Student: </td>
<td>#Html.DropDownListFor(model => model.StudentID, Model.Students, new { id = "ddlStudent" })</td>
</tr>
<tr>
<td>Year Completed:
</td>
<td>
#Html.DropDownListFor(model => model.YearCompleted, Model.Years, new { id = "ddlYear" })
</td>
</tr>
</table>
<table id="searchResults"></table>
<div id="searchResultPager"></div>
</div>
<div class="exportSearch">
<input type="button" value="Search" id="btnSearch" />
<input type="submit" value="Export" id="btnExport" />
</div>
}
Here is my Controller. As we don't have a database yet, I am just hardcoding some results while using an existing table from a different DB to provide record IDs.
[HttpGet]
public JsonResult GridData(string sidx, string sord, int? page, int? rows, string school, string student, string year, string option)
{
using (SMIDbContainer db = new SMIDbContainer())
{
var ds = (from sch in db.SCHOOLs
where sch.Active.HasValue
&& !sch.Active.Value
&& sch.LEVEL_9_ORGANISATION_ID > 0
select sch).ToList();
var jsonData = new
{
total = 1,
page = 1,
records = ds.Count.ToString(),
rows = (
from tempItem in ds
select new
{
cell = new string[]{
tempItem.LEVEL_9_ORGANISATION_ID.ToString(),
tempItem.SCHOOL_PRINCIPAL,
"40161",
"No",
"Passed (20)"
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
Is the JSON you are passing back to the grid valid? Are you passing back the information that the jqGrid needs? Why setup your jqGrid inside of an ajax call instead of inside your $(document).ready(function () { ?
Here is an example of the portion of code I use to format my json for jqGrid:
var jsonData = new
{
total = (totalRecords + rows - 1) / rows,
page = page,
records = totalRecords,
rows = (
from tempItem in pagedQuery
select new
{
cell = new string[] {
tempItem.value1,
tempItem.value2, ........
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
If you want to make your user search first, you can on the client side, set the jqgrid datatype: local and leave out the url. Then after your user does whatever you want them to do you can have the jqGrid go out and fetch the data, via something like:
$('#gridName').jqGrid('setGridParam', { datatype: 'json', url: '/Controller/getGridDataAction' }).trigger('reloadGrid', [{ page: 1}]);
If you want to pass in the search data, or other values to the controller/action that is providing the data to the jqGrid you can pass it via the postData: option in the jqGrid. To set that before going out you can set it via the setGridParam option as shown above via postData: { keyName: pairData}.
MVC and jqGrid work great...there are a ton of examples on stackoverflow and Oleg's answers are a vast resource on exactly what you are trying to do. No hole in desk via head banging required!