Trying to build a dynamic grid with my json store, but for some reason there's a range constraint and I guess it's part of the dgrid/store implementation, but I should scroll down my grid and get more result from the example at dgrid site.
I'll put some code in here. First, I tried to be very modular in my code, so I have a file that gets my store (content.js), a file that build my grid (gridlayout.js) and main.js (create my instance and pass my store).
content.js
define([
"dojo/_base/xhr",
"dojo/store/Memory",
"dojo/store/JsonRest",
"dojo/store/Cache"
],
function(
xhr,
Memory,
JsonRest,
Cache
){
var contentMemoryStore = new Memory();
var contentJsonRestStore = new JsonRest({target: "http://dev.mpact.tv:30087/rest/contenus/"});
contentStore = new Cache(contentJsonRestStore, contentMemoryStore);
return contentStore;
});
GridLayout.js
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dgrid/OnDemandGrid",
"dgrid/Keyboard",
"dgrid/Selection",
"dgrid/extensions/ColumnHider",
"dgrid/editor",
], function(
declare,
_WidgetBase,
Grid,
Keyboard,
Selection,
Hider,
editor
){
return declare([Grid, Keyboard, Selection, Hider], {
columns: {
selected: editor({
label: " ",
autoSave: true,
sortable: false
}, "checkbox"),
nom: "Name",
autodelete: "Auto-delete",
groupe_id: "Groupe ID",
global: "Global",
date: "Date",
duree: "Lenght",
description: "Description",
fichier: "Filename",
pleinecran: "Fullscreen",
repertoire: "Folder",
taille: "Size",
expiration: "Expired",
id: "id",
catergorie: "Category",
brouillon: "Example"
},
});
});
and my main.js:
var gridLayout = new GridLayout({}, "placeholder");
gridLayout.set("store", contentStore);
So far, I just 25 result and if I scroll down, I don't get the rest of my items.
The answer in your browser shows items 0-24/25. That means the total number of items server side is 25. Hence the grid won't try to fetch more than that.
If it were returning 0-24/1000, then there would be multiple calls when you would scroll.
So I think you should check server side why it only returns 25 as total number of items.
Check this: http://dojotoolkit.org/reference-guide/1.7/dojo/store/JsonRest.html#id7
Related
Ok I'm pretty sure I know exactly what I need to do here but I'm not sure how to do it. Basically I have a grid that I want to make a key column bind to an array of key/values, which I've done before with kendo (not using Angular) and I know that when I'm creating my key/value array asynchronously then that needs to complete before I can get them show-up with kendo, which I have done using promises before.
So here I have the same issue only angular is also involved. I need to fetch and format an array of data into the format in which a kendo grid column can digest it, so no problem here is my controller code:
var realm = kendo.data.Model.define({
id: 'realmID',
fields: {
realmID: { editable: false, nullable: true }
realmType: { type: 'string', validation: { required: true } }
}
})
var ds1 = kendoHelpers.dataSourceFactory('realms', realm, 'realmID')
var realmType = kendo.data.Model.define({
id: 'realmTypeID',
fields: {
realmTypeID: { editable: false, nullable: true },
name: { type: 'string', validation: { required: true } }
}
})
var ds2 = kendoHelpers.dataSourceFactory('realms/types', realmType, 'realmTypeID')
$scope.mainGridOptions = {
dataSource: ds1,
editable: true,
navigatable: true,
autoBind:false,
toolbar: [
{ name: "create" },
{ name: 'save' },
{ name: 'cancel' }
],
columns: [
{ field: 'realmID', title: 'ID' }
{ field: 'realmTypeID', title: 'Realm Type', editor: realmTypesDDL, values: $scope.realmTypeValues },
{ command: "destroy" }
]
}
$scope.secondGridOptions = {
dataSource: ds2,
editable: true,
navigatable: true,
toolbar: [
{ name: "create" },
{ name: 'save' },
{ name: 'cancel' }
],
columns: [
{ field: 'realmTypeID', title: 'ID' },
{ field: 'name', title: 'Name' }
{ command: "destroy" }
]
}
ds2.fetch(function () {
$scope.realmTypeValues = [{ text: 'Test', value: "24bc2e62-f761-4e70-804c-bc36fdeced3d" }];
//this.data().map(function (v, i) {
// $scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID})
//});
//$scope.mainGridOptions.ds1.read()
});
function realmTypesDDL(container, options) {
$('<input />')
.appendTo(container)
.kendoDropDownList({
dataSource: ds2,
dataTextField: 'name',
dataValueField: 'realmTypeID'
});
}
I made this dataSourceFatory helper method above to return me a basic CRUD kendo dataSource that uses transport and also injects an authorization header which is working fine so don't get hung up on that, ultimately I'm going to be using this data in another grid as well as for reference values for the main grid, but I've hard coded some values that I can use to test with in the ds2.fetch callback.
My HTML is pretty plain:
<div>
<h2>Realms</h2>
<kendo-grid options="mainGridOptions"></kendo-grid>
<h2>Realm Types</h2>
<kendo-grid options="secondGridOptions"></kendo-grid>
</div>
This all works fine and well except I am only seeing the GUID of the realmTypeID in the grid, I click it and the editor is populated correctly so that's good but I want the text value to be displayed instead of the GUID. I'm sure the issue is that the array of values is empty whenever angular is binding to the grid options. My questions are:
How do I either delay this bind operation or manually rebind it after the fetch call?
Is there a better way to handle a situation like this? I try not to expend finite resources for no reason (IE making server calls when unnecessary)
Note: When I move the creation of the text/value array to happen before the grid options, I get the desired behavior I am after
EDIT A work around is to not use the directive to create the grid and instead defer the grid creation until the callback of whatever data your column is dependent on, I was hoping for a more elegant solution but this is better than nothing. So your HTML becomes something like
<h2>Realms</h2>
<div id="realms"></div>
<h2>Realm Types</h2>
<kendo-grid options="secondGridOptions"></kendo-grid>
Then you can create the grid in the fetch callback for example:
ds2.fetch(function () {this.data().map(function (v, i) {
$scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID})
});
$('#realms').kendoGrid($scope.mainGridOptions);
$scope.mainGridOptions.dataSource.fetch()
});
But this doesn't feel very angularish so I'm really hoping for a better solution!
Ok...well I think I hacked this enough and without another suggestion I'm going to go forward with this approach. I'm just going to move the binding logic to the requestEnd event of the second grid so that the values array can be populated right before the binding even. I'm also reworking the values array in this method. It is a bit weird though, I think there is some kendo black magic going on with this array because I can't just set it to a new empty array without it breaking completely...which is why I'm poping everything out prior to repopulating the array. That way when something is deleted or edited in the second grid, the DDL in the first grid is updated in the callback.
function requestEnd(e) {
for (var i = $scope.realmTypeValues.length; i >= 0; i--) $scope.realmTypeValues.pop();
var data;
if (e.type == "read")
data = e.response;
else
data = e.sender.data();
data.map(function (v, i) { $scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID }); });
if ($('#realms').data('kendoGrid') == undefined) {
$('#realms').kendoGrid($scope.mainGridOptions);
}
else
$('#realms').data('kendoGrid').columns[4].values = $scope.realmTypeValues;
}
ds2.bind('requestEnd', requestEnd);
So I'm going to accept my own answer unless anyone has a better approach!
EDIT: ANSWER FOUND
I found the answer in this post. there is a private store config field called remoteSort that is set to true by default, so client-side sorters won't get used unless remoteSort is explicitly set to false.
I'm trying to sort a grid of Features by the number of User Stories in them. I've tried a couple different things. The first was a sorter function in the data Store Configuration:
Ext.create('Rally.data.wsapi.Store',{
model: 'PortfolioItem/Feature',
autoLoad:true,
start: 0,
pageSize: 20,
fetch: ['Name', 'ObjectID', 'Project', 'Release', 'UserStories','State','_ref'],
context://context stuff
filters:[
//filter stuff
],
sorters:[
{
property:'UserStories',
sorterFn: function(o1, o2){
return o1.get('UserStories').Count - o2.get('UserStories').Count;
}
}
],
listeners: //listener stuff
But this always returned nothing. (when the sorter was not included, i did get back all the Correct Features, but I could not sort the the number of user stories per Feature).
I also tried adding a sorter to the column in the grid, as seen in this post:
xtype: 'rallygrid',
width:'50%',
height:400,
scroll:'vertical',
columnCfgs: [
{
text:'Stories',
dataIndex:'UserStories',
xtype:'numbercolumn',
sortable:true,
doSort: function(direction){
var ds = this.up('grid').getStore();
var field = this.getSortParam();
console.log(ds, field, direction);
ds.sort({
property: field,
direction:direction,
sorterFn: function(us1, us2){
return (direction=='ASC'? 1 : -1) * (us1.get(field).Count - us2.get(field).Count);
}
});
},
width:'20%',
renderer:function(us){
return us.Count;
}
}
]
But I was having the same issues that the person in the other thread was having, where nothing was getting sorted.
I'm trying to connect a dojo dgrid to a solr data service and need some help.
When I use jsonp I can get connected to the solr data and output the data result to the screen with something like this:
dojo.require("dojo.io.script");
function searchGoogle(){
// Look up the node we'll stick the text under.
var targetNode = dojo.byId("output");
// The parameters to pass to xhrGet, the url, how to handle it, and the callbacks.
var jsonpArgs = {
url: "myExternalSolrURL",
callbackParamName: "json.wrf",
content: {
wt: "json",
rows: "12",
start: "1",
q: "*"
},
load: function(data){
// Set the data from the search into the viewbox in nicely formatted JSON
targetNode.innerHTML = "<pre>" + dojo.toJson(data, true) + "</pre>";
},
error: function(error){
targetNode.innerHTML = "An unexpected error occurred: " + error;
}
};
dojo.io.script.get(jsonpArgs);
}
dojo.ready(searchGoogle);
But, when I try to use jsonrest to connect to the solr data and get it to show up in a dgrid nothing appears to happen. This is the code I have for that:
<script>
var myStore, dataStore, grid;
require([
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dgrid/Grid",
"dojo/data/ObjectStore",
"dojo/query",
"dijit/form/Button",
"dojo/domReady!"
], function (JsonRest, Memory, Cache, Grid, ObjectStore, query, Button, domReady) {
myStore = Cache(JsonRest({
target: "myExternalSolrURL",
idProperty: "id"
}),
Memory({ idProperty: "id" }));
grid = new Grid({
store: dataStore = ObjectStore({ objectStore: myStore }),
structure: [
{ name: "Thing id", field: "id", width: "50px" },
{ name: "Name", field: "name", width: "200px" },
{ name: "detail", field: "detail", width: "200px" }
]
}, "grid"); // make sure you have a target HTML element with this id
grid.startup();
});
</script>
<div style="height: 300px; width: 600px; margin: 10px;">
<div id="grid">
</div>
</div>
Does anyone see what I am missing?
You changed your code to use dgrid, but it looks like you are still attempting to use a dojo/data store with dgrid. dgrid only supports the dojo/store API, so stop wrapping your store in ObjectStore.
dgrid/List and dgrid/Grid do not contain store logic. You will want to either use dgrid/OnDemandGrid or mix in dgrid/extensions/Pagination.
Make sure the service you are using with dojo/store/JsonRest actually behaves as the store implementation expects (or use or write a diffrent dojo/store implementation)
Apparently part of the problem is that a Solr index is not a flat data structure like a grid or dgrid can deal with. When you have nested data returned like a Solr or ElasticSearch index will return it must be "flattened" to go into a grid. However, this sort of hierarchy of data will work with a tree vs a grid. So the next challenge is to connect to the index and flatten it.
This is my code for combo box inside grid:
{
header: 'FSCS',
dataIndex: 'acntOvrrideTypeCd',
flex: 1,
renderer: function(val, metaData, record, rowIndex, colIndex) {
var id = Ext.id();
var store = new Ext.data.Store({
fields: ['code', 'description'],
data: [{
"code": "",
"description": ""
}, {
"code": "E",
"description": "E"
}, {
"code": "D",
"description": "D"
}, {
"code": "S",
"description": "S"
}]
});
Ext.Function.defer(
(function() {
var cb = Ext.create('Ext.form.ComboBox', {
id: 'acntOvrrideTypeCd-' + rowIndex,
queryMode: 'local',
renderTo: id,
store: store,
forceSelection: true,
triggerAction: 'all',
lazyRender: true,
size: 5,
valueField: 'code',
displayField: 'description',
value: val
//listeners:{
// scope: this,
// 'select': Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex)
//}
});
cb.on(afterrender, function() {
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
});
}), 0.25);
console.log("i----------" + id);
return (Ext.String.format('<div id="{0}"></div>', id));
}
}
'afterrender' event is not fired. I need to enable or disable component after its rendered.
Can anyone help?
It's just a typo, afterrender should be in quotes otherwise you will just add the function for undefined event.
cb.on('afterrender',function(){
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
});
There are a few problems with your code.
It looks like you're trying to create a combobox in the renderer function of a grid (your code at the top didn't get included in the code block). You're better off using the Ext.grid.plugin.CellEditing plugin instead, which will create a field on demand instead of when the column renders. Plus, every time your grid view refreshes you'll be creating another store and combobox for every row in the grid. Not good for performance, not good for the user experience either.
When calling defer, the duration is in milliseconds, not seconds. Also, you don't need to wrap the function in parenthesis. Just give it the function itself. Like this:
Ext.defer(function(){
// do stuff
}, 25);
Setting lazyRender to true only works if your component is the child of some container that doesn't render all its components immediately (like a tabpanel).
It may be easier to just set the disabled config in the combobox when you create it instead of when you render it, unless you don't have the information available at creation time.
Like nscrob said, when using the on method you need to specify the event as a string. If you use the listeners config (which you have commented out), you can just do:
listeners: {
afterrender: function(){
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
},
select: function(){
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
}
}
It's important to note that the scope of these listener functions defaults to the component itself (your combobox) so scope: this is unnecessary. Unless you want the scope to be whatever object is creating this combobox, that is.
The first point is the most important. Look into using the CellEditing (or RowEditing) plugin and I guarantee things will go a lot more smoothly.
NOTE: Author is new to EXT JS and is trying to use MVC in his projects
imagine i have a web service whose data model is not fixed. I would want to have my models dynamically created, from which i dynamically create stores and hence dynamic components whose data is in those stores.
Lets start by seeing a sample class definition of a model:
Ext.define('MNESIA.model.User', {
extend: 'Ext.data.Model'
});
In this model definition, i have left out the 'fields' parameter in the config object. This is because whwnever i create an instance of a model of the type above, i want to dynamically give it its fields definition, in other words i can have many instances of this model yet all having different definition of their 'fields' parameter.
From here i create a definition of the store, like this:
Ext.define('MNESIA.store.Users', {
extend: 'Ext.data.Store',
autoLoad: true
}
});
There, i have a store definition. I have left out the 'model' parameter because i will attach it to every instance of this class dynamically. Infact, even the 'data' and 'proxy' settings are not mentioned as i would want to asign them during the instantiation of this store.
From there, i would want to have dynamic views, driven by dynamic stores. Here below i have a definition of a Grid
Ext.define('MNESIA.view.Grid' , {
extend: 'Ext.grid.Panel',
alias : 'widget.mygrid',
width: 700,
height: 500
});
I have left out the following parameters in the Grid specification: 'columns', 'store' and 'title' . This is because i intend to have many Grids created as instances of the specification above yet having dynamic stores, titles and column definitions.
Some where in my controller, i have some code that appears like this:
function() {
var SomeBigConfig = connect2Server();
/*
where:
SomeBigConfig = [
{"model":[
{"fields":
["SurName","FirstName","OtherName"]
}
]
},
{"store":[
{"data":
{"items":[
{"SurName":"Muzaaya","FirstName":"Joshua","OtherName":"Nsubuga"},
{"SurName":"Zziwa","FirstName":"Shamusudeen","OtherName":"Haji"},
...
]
}
},
{"proxy": {
"type": "memory",
"reader": {
"type": "json",
"root": "items"
}
}
}
]
},
{"grid",[
{"title":"Some Dynamic Title From Web Service"},
{"columns": [{
"header": "SurName",
"dataIndex": "SurName",
"flex": 1
},{
"header": "FirstName",
"dataIndex": "FirstName",
"flex": 1
},
{
"header": "OtherName",
"dataIndex": "OtherName",
"flex": 1
}
]}
]
}
]
*/
var TheModel = Ext.create('MNESIA.model.User',{
fields: SomeBigConfig[0].model[0].fields
});
var TheStore = Ext.create('MNESIA.store.Users',{
storeId: 'users',
model: TheModel,
data: SomeBigConfig[1].store[0].data,
proxy: SomeBigConfig[1].store[1].proxy
});
var grid = Ext.create('MNESIA.view.Grid',{
store: TheStore,
title: SomeBigConfig[2].grid[0].title,
columns: SomeBigConfig[2].grid[1].columns
});
// From here i draw the grid anywhere on the, page say by
grid.renderTo = Ext.getBody();
// end function
}
Now this kind of dynamic creating of models, then stores, and then grids does result into memory wastage and so this would require the destroy methods of each component to be called each time we want to destroy that component.
Questions:
Qn 1: Does the MVC implementation of EXT JS 4 permit this ?
Qn 2: How would i gain the same functionality by using the xtypes of my new classes. Say for example:
{
xtype: 'mygrid',
store: TheStore,
title: SomeBigConfig[2].grid[0].title,
columns: SomeBigConfig[2].grid[1].columns
}
Qn 3: If what i have written above really works and is pragmatically correct, can i apply this to all components like Panels, TabPanels, Trees (whereby their Configs are sent by a remote server) ?
Qn 4: If i have Controllers A and B, with controller A having a specification of views: [C, D] and Controller B having views: [E, F], would it be correct if actions generated by view: E are handled by Controller A ? i.e. Can a controller handle actions of a view which is not registered in its views config ?
NOTE: am quite new to Ext JS but would love to learn more. Advise me on how to improve my EXT JS learning curve. Thanks
In my option ,your model must map onto the store that is to be rendered to the view like for example,if implement the model part like this
{"model":[{"fields":[{name:'name',type:'string'},
{name:'id',type:'string'}]}]} ,this will be easily mapped onto the store for the view render it.