ExtJS 4: Changing Store param names - extjs4

Right now I'm running into a problem where I can't seem to change the param names page, start, limit, and dir for a Ext.data.Store.
In ExtJS 3 I could do this:
paramNames :
{
start : 'startIndex',
limit : 'pageSize',
sort : 'sortCol',
dir : 'sortDir'
}
I tried adding this configuration to the Ext.data.Store for ExtJS 4 however 'start', 'limit', 'sort', and 'dir' still show up as the default param names. I need to be able to change this as the server side functionality requires these param names. This also causes paging and remote sorting to not work since the param names don't match what the server side resource is expecting.
So is there a new way in ExtJS 4 to change these param names like in ExtJS 3?

take a look at Proxy,
see http://docs.sencha.com/ext-js/4-0/#/api/Ext.data.proxy.Server
directionParam,limitParam...

To dynamically modify the parameters just before the load of a store you can do this:
/* set an additional parameter before loading, not nice but effective */
var p = store.getProxy();
p.extraParams.searchSomething = search;
p.extraParams.somethingelse = 'This works too';
store.load({
scope : this,
callback: function() {
// do something useful here with the results
}
});

Use this code:
proxy: {
type: 'ajax',
url: '/myurl',
method: 'GET',
**extraParams: { myKeyword: 'abcd' },**
reader: {
type: 'json',
root: 'rows'
}
}
Now you can change your myKeyword value from abcd to xyz in following way.
gridDataStore.proxy.extraParams.keyword='xyz';
gridDataStore.load();
this will set your parameters' value and reload the store.

The keys were renamed and moved to the Ext.data.Proxy object. Here's a simple example that tells ExtJS to use the default Grails parameter names:
Ext.create('Ext.data.Store', {
// Other store properties removed for brevity
proxy: {
// Other proxy properties removed for brevity
startParam: "offset",
limitParam: "max",
sortParam: "sort",
directionParam: "order",
simpleSortMode: true
}
});
I also set the simpleSortMode so that each of the parameters are sent to the server as discrete request parameters.

Related

What is the proper way of adding a custom field to Keystone (to be included in an admin UI form)?

I can see the nice explanation for fields, and what they are made of, here: https://github.com/keystonejs/keystone/tree/v4.0.0-beta.5/fields
How do you go about adding a custom field?
Is adding a custom field (versioned in my own project which depends on keystone, or perhaps done generic enough that could be pushed to npm) a matter of importing it during the keystone setup script and somehow mutating the keystone instance or whatever in order that it also loads my field along with the built-in ones?
EDIT:
The use case is in the context of the admin UI (e.g. you have a User keystone model, and you want the User form to have a new custom field whose UI is an arbitrary react component you implement)
The framework does support storage fields like local file, s3, azure, cloudinary images and embedly fields. That might satisfy your file field needs.
Custom Fields
It appears that the keystone wiki has a short tutorial on the keystonejs wiki and at time of writing, custom types aren't supported in the admin UI.
The example code in the wiki includes a validation method for a credit card number, so this might be the type of functionality that you're looking for.
Here's a short example of what a custom type would look like. It's a field that only accepts Jeff or Alexander as a valid value. You would put it in its own myNameType.js file.
var keystone = require('keystone');
var util = require('util');
/*
Custom FieldType Constructor
#extends Field
#api public
*/
function myName(list, path, options) {
// add your options to this
// call super_
this._nativeType = Text;
myName.super_.call(this, list, path, options);
}
/* inherit Field */
util.inherits(myName, keystone.Field);
/* override or add methods */
myName.prototype.validateInput = function(data) {
console.log('validate my name');
var isValid = false;
if (data && (data.toLower() === 'jeff' || data.toLower() === 'alexander')) {
isValid = true;
}
return isValid;
};
Then register your type in the keystonejs startup file:
// Require keystone
var keystone = require('keystone');
// add a new custom Field Type
Object.defineProperty(
keystone.Field.Types,
'MyName',
{
get: function() {
// or whatever your path is
return require('./myName.js');
}
}
);
From there you can use it in a model (remember to set it to hidden because of the lack of admin UI support):
var keystone = require('keystone');
var Types = keystone.Field.Types;
var Person = new keystone.List('Post', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true },
sortable: 'unshift',
perPage: 5,
track: true,
autocreate: true
});
Person.add({
name: { type: Types.MyName, label: 'My Name', hidden: true },
heightInInches: { type: Types.Number, label: 'Height (inches)' },
});
Person.register();

Dynamically add properties to a controller in Ember.js

I am creating an application in Ember that needs to be easily replicated with slight variations on different sites. What I'd like to do is, essentially, have my Ember app read a config file and then adjust itself accordingly.
In my case, I only need to change one controller and an accompanying template. In this question, I am tackling the controller only.
I need to add properties to a controller dynamically that can then be called in a template.
How would I go about doing that?
Here's what I came up with:
var propertiesConfig = [
{name: 'newFunction1', arbitraryType: 'monkeys', numThings: 6 },
{name: 'newFunction2', arbitraryType: 'daysOfChristmas', numThings: 7 },
{name: 'newFunction3', arbitraryType: 'monkeys', numThings: 8 },
{name: 'newFunction4', arbitraryType: 'daysOfChristmas', numThings: 9 },
{name: 'newFunction5', arbitraryType: 'monkeys', numThings: 10 },
{name: 'newFunction6', arbitraryType: 'daysOfChristmas', numThings: 11 },
{name: 'newFunction7', arbitraryType: 'monkeys', numThings: 12 }
];
var MonkeyFunctionBuilder = function(buildData){
return function(){
if (buildData.numThings == 12) return 'Brad Pitt';
return buildData.numThings + ' monkeys';
}
};
var ChristmasDaysFunctionBuilder = function(buildData){
return function(){
return buildData.numThings + ' ' + daysOfChristmas[buildData.numThings]
}
};
var passedObject = {};
for(var i = 0; i < propertiesConfig.length; i++){
switch(propertiesConfig[i].arbitraryType){
case 'monkeys':
passedObject['monkeys'+propertiesConfig[i].numThings] =
new MonkeyFunctionBuilder(propertiesConfig[i]).property();
break;
case 'daysOfChristmas':
passedObject[propertiesConfig[i].name] =
new ChristmasDaysFunctionBuilder(propertiesConfig[i]).property();
break;
}
};
App.AnyController.reopen(passedObject);
The propertiesConfig represents my config file mentioned in the question.
I found that I needed to create the builder functions (actually Classes, if I am not mistaken) to keep the values on already created properties from getting updated whenever i changed inside my for loop. These functions' names need to start with a CaptialLetter, not the normal uncapitalized first letter that is mostly seen in javascript code.
MonkeyFunctionBuilder uses only the data found inside the config object to build the function. ChristmasDaysFunctionBuilder uses data from the daysOfChristmas object that was declared somewhere else in the application. In theory, that could be called from an API so that your application can be altered from an external set of data as well. Be careful: security, etc.
Note also that when using a variable to declare or get the data from the name of a property on any object, you have to use brackets (object[variableName]) instead of the normal dot notation (object.variableName). Found that out from another question on StackOverflow.
In the final block of code where you see the loop, you can do whatever you need to do to manipulate your data. In my example, I am choosing to use propertiesConfig[i].name to declare some of the App.AnyController property names, and then propertiesConfig[i].numThings (along with the keyword monkey) to declare the others.
The result inside the Ember Debugger on the AnyController looks like this:

How to access the elements in a sencha touch 2 store

I am new to Sencha Touch so I am still struggling with the usage of stores.
I have created this store which I am successfully using to populate a list:
Ext.define('EventApp.store.events',{
extend: 'Ext.data.Store',
config: {
model: 'EventApp.model.event',
autoLoad: true,
storeId: 'events',
proxy:{
type:'ajax',
url: './resources/EventData.json',
reader: {
type: 'json',
rootProperty: 'events'
}
}
}
});
As I mentiones this store works correctly when referenced from a list and I can display the contents of it. Therefore I am assuming the store is correctly defined.
Unfortunately when I try to access the store from a controller for one of my views (which will be used to populate the items of a carousel) I don't seem to get any data back from the store. The code I am using is the following:
onEventCarouselInitialize : function(compon, eOptions) {
var past = compon.getPast();
var eventsStore = Ext.getStore('events');
eventsStore.each(function(record){
console.log('Record =',record); //<-- this never gets executed.
},this);
}
I have tried executing an eventsStore.load() on an eventsStore.sync() but I never seem to get any elements available in the store.
What am I missing?
Thanks
Oriol
What i have understand is, perhaps your store data has not been loaded when you are accessing it. So put you each() function on store inside this for delaying 500ms:
Ext.Function.defer(function(){
// Put each() here
}, 500);
Have a try by delaying more or less.

Loading store with params

What I am trying to do is load store with params like below so I only get the first ten items of my store.
app.stores.actualites.load({
params : {
start:0,
limit:10,
},
callback : function(records, operation, success) {
app.loadmask.hide();
}
});
But this is not working, it returns all the 18 store items.
If I put the start param to 1, it will return 17 items, so this param is working but not the other.
Update : Store code
app.stores.actualites = new Ext.data.Store({
model: 'app.models.Actualites',
proxy: {
type: 'ajax',
url: app.stores.baseAjaxURL + '&jspPage=%2Fajax%2FlistActualites.jsp',
reader: {
type: 'json',
root: 'actualite',
successProperty: 'success',
totalProperty: 'total',
idProperty: 'blogEntryInfosId'
}
}
});
The weird thing here is when I try the URL in a browser and add &start=0&limit=1 it works just fine...
Update : Try with extraParams
I also tried to do it with extraParams but this still doesn't work
app.stores.actualites.getProxy().extraParams.start = 1;
app.stores.actualites.getProxy().extraParams.limit = 2;
app.stores.actualites.load({
callback : function(records, operation, success) {
app.loadmask.hide();
}
});
The pagination functionality has to be actually implemented at your server side. Sencha will only maintain the pages and will send you proper start and limit values. You need to access these values at your server side script and return appropriate results depending on those.
If you are using a list, then you can use Sencha's inbuilt ListPaging plugin which takes care of the start/limit parameter in its own.
This might sound weird, but I changed to name of the param 'limit' to 'stop' both on the client and the server and it worked...
it should be something like that:
app.stores.actualites.getProxy().setExtraParams({
start:1,
limit:2
})

sencha list paging plugin

I'm trying to use sencha touch's listpaging plugin. But there is almost no good( or bad ) documentation about how to use it and i'm confused.
When i activate the plugin in my list
this.myList = new Ext.List({
store: this.myStore,
plugins: [{
ptype: 'listpaging',
autoPaging: false
}],
itemTpl: '...'
});
a 'Load More' text and a loading image is added to the end of the list.
But i don't know how to configure my store to enable this plugin to be able to load more data.
App.regStore('MyStore', {
model: 'myModel',
proxy: {
type: 'ajax',
url: 'http://mydomain.com/json?howmany=10&page=1',
reader: {
type: 'json',
root: 'results'
}
}
});
App.stores.myStore = Ext.StoreMgr.get('MyStore');
How can i configure my store so when i tap "Load more", the store brings up page 2 and add them to the list automatically?
I've had a similar issue with the ListPaging plugin in SenchaTouch 2, and managed to sort out the 'load more' message behaviour when the end of the data set is reached.
This builds on what John Gordon has come up with (regarding specifying the pageSize and clearOnPageLoad config options), since these properties seem to be the same in Senchatouch 2. I haven't looked at SenchaTouch 1.x in detail. In SenchaTouch 2, all config options must be defined in a config property:
Ext.define('MessagingApp.store.MessageThreads', {
extend : 'Ext.data.Store',
requires: ['MessagingApp.model.MessageThread'],
config:
{
model: 'MessagingApp.model.MessageThread',
autoLoad: false,
clearOnPageLoad: false, // This is true by default
pageSize: 10, // This needs to be set for paging
proxy: {
type: 'jsonp',
pageParam: 'currentPage',
limitParam: 'pageSize',
url: APIURL + '/message-app-service/GetMessageThreads',
reader: {
type: 'json',
rootProperty: 'Data'
}
}
}
});
In the view, where we specify the plugins, we can override the 'load more' and 'no more records' messages:
{
xtype:'dataview',
store:'MessageThreads',
id:'threadList',
itemTpl:Ext.create('Ext.XTemplate',
'<!-- template markup goes here -->',
{
//custom helper functions here
}
),
plugins:[
{
xclass:'Ext.plugin.ListPaging',
autoPaging: true,
// These override the text; use CSS for styling
loadMoreText: 'Loading...',
noMoreRecordsText: 'All messages loaded'
}
]
}
The issue is that while our web service returns an array of records for a particular page, there is no overall total count property, which is needed for the plugin to determine when all records have been loaded. Hence as it is, the 'Load more' message will remain (issue #1 in the accepted solution). So in the init function of our controller, we have to attach an event handler for the load event on the store to hook into when a new page of data is received:
Ext.define('MessagingApp.controller.Messages',
{
extend: 'Ext.app.Controller',
config:
{
views: [
'MessageThreads',
// Other views referenced by this controller
],
stores: [
'MessageThreads'
],
refs:
{
// References to UI elements by selector...
}
},
init: function() {
// Other internal initialisation...
this.control(
{
// Selector-object pairs...
});
// Provide a means to intercept loads of each page of records
var threadStore = Ext.getStore('MessageThreads');
threadStore.addBeforeListener('load', this.checkForThreadListEnd, this);
},
// Remaining controller functions...
});
In the handler, we realise that we've reached the end of the data set when the number of records returned is less than the page size. If the total record count is a multiple of the page size, the last 'page' will have an empty array. Once the end of the data set has been identified, we update the totalCount config property of the store:
checkForThreadListEnd: function(store, records, isSuccessful) {
var pageSize = store.getPageSize();
var pageIndex = store.currentPage - 1; // Page numbers start at 1
if(isSuccessful && records.length < pageSize)
{
//Set count to disable 'loading' message
var totalRecords = pageIndex * pageSize + records.length;
store.setTotalCount(totalRecords);
}
else
store.setTotalCount(null);
},
// Other controller functions...
Because this is a 'before' event handler, this property will be crucially updated before the plugin decides whether to display the 'load more' or 'no more records' messages. Unfortunately, the framework doesn't provide a means to hide the 'no more records' message, so this would have to be done via CSS.
Hope this helps.
I'm having problems finding good documentation, too, but I can at least answer your question. You need to add pageSize to your store, clearOnPageLoad as well, if you want to not clear out what was already loaded. Her's my code:
Ext.regStore('publicresources', {
model: 'PublicResource',
autoLoad:false,
remoteFilter:true,
sortOnFilter:true,
pageSize: 15,
clearOnPageLoad: false,
sorters: [
{
property : 'distance',
direction: 'ASC'
}
]
});
My outstanding issues that I'm looking into are:
How to turn off the "More" element when there are no more records to load
How to detect that there are no more records to load, and where to put that detection code.
How to keep the list at the location that the user was at. Each load jumps back to the 1st item in the list
Good luck!
Remember also that this works only server side currently.
Forum thread http://www.sencha.com/forum/showthread.php?105193-Store-pageSize
In regards to the "load more vs. no more records" message -
If you are writing a custom proxy (example here A Sencha Touch MVC application with PhoneGap), you set the total records in the returned Operation.
If the total records are not yet known, you can do something like the below, where,
1) if you are returning the requested limit of records, set the total to something larger than the records the store will now hold
2) if returning < the requested limit of records, set the total to 1 (to force the "no more records message")
//return model instances in a result set
operation.resultSet = new Ext.data.ResultSet({
records: contacts,
//total : contacts.length,
total : contacts.length === operation._limit ? (operation._limit * operation._page +1) : 1,
loaded : true
});
//announce success
operation.setSuccessful();
operation.setCompleted();
//finish with callback
if (typeof callback == "function") {
callback.call(scope || thisProxy, operation);
}
Just to add what worked for me..
If your server returns a totalCount and you want to set it you can use the totalProperty in the reader
reader: {
type: 'json',
rootProperty: 'results',
totalProperty: 'resultCount'
}