Using on the dashboard example, i'm trying to generate a treelist menu, based on user privileges.
After successfully log in, the main view is generated. The main, contains in the west region the treelist menu and next to it, the data panel. The navigation is done by using hashtags. The problem apear on refresh or in the first initialization. Actually, i noticed that the navigation store is loaded after the view is rendered.
How / where do i get to load the navigation store, so on refresh or first initalization of the view, i can get it and using it to match the routes?
Thanks,
M
My main view looks like this:
Ext.define('app.view.main.Main', {
extend: 'Ext.container.Viewport',
xtype: 'app-main',
id:'app-main',
requires: [
'Ext.button.Segmented',
'Ext.list.Tree'
],
controller: 'main',
viewModel: 'main',
cls: 'sencha-dash-viewport',
itemId: 'mainView',
layout: {
type: 'vbox',
align: 'stretch'
},
listeners: {
render: 'onMainViewRender'
},
items: [
{
xtype: 'toolbar',
cls: 'sencha-dash-dash-headerbar shadow',
height: 64,
itemId: 'headerBar',
items: [
{
xtype: 'tbtext',
text: localStorage.getItem('Name'),
cls: 'top-user-name'
},
{
xtype: 'image',
cls: 'header-right-profile-image',
height: 35,
width: 35,
alt:'current user image',
src: 'resources/images/user-profile/mb.jpg'
}
]
},
{
xtype: 'maincontainerwrap',
id: 'main-view-detail-wrap',
reference: 'mainContainerWrap',
flex: 1,
items: [
{
xtype: 'treelist',
reference: 'navigationTreeList',
itemId: 'navigationTreeList',
width: 250,
expanderFirst: false,
expanderOnly: true,
ui: 'navigation',
bind: '{navItems}',
listeners: {
selectionchange: 'onNavigationTreeSelectionChange'
}
},
{
xtype: 'container',
reference: 'mainCardPanel',
flex:1,
cls: 'sencha-dash-right-main-container',
itemId: 'contentPanel',
layout: {
type: 'card',
anchor: '100%'
}
}
]
}
]
});
The viewmodel:
Ext.define('app.view.main.MainModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.main',
stores: {
navItems: {
type: 'tree',
storeId: 'NavigationTree',
name: 'NavigationTree',
root: {
expanded: true
},
autoLoad: false,
proxy: {
type: 'ajax',
url: 'php.php',
reader: {
type: 'json',
idProperty: 'id',
messageProperty: 'msg'
}
}
}
}
});
And the viewcontroller:
Ext.define('app.view.main.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
listen : {
controller : {
'#' : {
unmatchedroute : 'onRouteChange'
}
}
},
routes: {
':node': 'onRouteChange'
},
lastView: null,
setCurrentView: function(hashTag) {
hashTag = (hashTag || '').toLowerCase();
var me = this,
refs = me.getReferences(),
mainCard = refs.mainCardPanel,
mainLayout = mainCard.getLayout(),
navigationList = refs.navigationTreeList,
store = me.getViewModel().getStore('navItems');
//store = navigationList.getStore();
var node = store.findNode('routeId', hashTag) ||
store.findNode('viewType', hashTag);
var view = (node && node.get('viewType')) ,
lastView = me.lastView,
existingItem = mainCard.child('component[routeId=' + hashTag + ']'),
newView;
// Kill any previously routed window
if (lastView && lastView.isWindow) {
lastView.destroy();
}
lastView = mainLayout.getActiveItem();
if (!existingItem) {
newView = Ext.create({
xtype: view,
routeId: hashTag, // for existingItem search later
hideMode: 'offsets'
});
}
if (!newView || !newView.isWindow) {
// !newView means we have an existing view, but if the newView isWindow
// we don't add it to the card layout.
if (existingItem) {
// We don't have a newView, so activate the existing view.
if (existingItem !== lastView) {
mainLayout.setActiveItem(existingItem);
}
newView = existingItem;
}
else {
// newView is set (did not exist already), so add it and make it the
// activeItem.
Ext.suspendLayouts();
mainLayout.setActiveItem(mainCard.add(newView));
Ext.resumeLayouts(true);
}
}
navigationList.setSelection(node);
if (newView.isFocusable(true)) {
newView.focus();
}
me.lastView = newView;
},
onNavigationTreeSelectionChange: function (tree, node) {
var to = node && (node.get('routeId') || node.get('viewType'));
if (to) {
this.redirectTo(to);
}
},
onToggleNavigationSize: function () {
var me = this,
refs = me.getReferences(),
navigationList = refs.navigationTreeList,
wrapContainer = refs.mainContainerWrap,
collapsing = !navigationList.getMicro(),
new_width = collapsing ? 64 : 250;
if (Ext.isIE9m || !Ext.os.is.Desktop) {
Ext.suspendLayouts();
refs.senchaLogo.setWidth(new_width);
navigationList.setWidth(new_width);
navigationList.setMicro(collapsing);
Ext.resumeLayouts(); // do not flush the layout here...
// No animation for IE9 or lower...
wrapContainer.layout.animatePolicy = wrapContainer.layout.animate = null;
wrapContainer.updateLayout(); // ... since this will flush them
}
else {
if (!collapsing) {
// If we are leaving micro mode (expanding), we do that first so that the
// text of the items in the navlist will be revealed by the animation.
navigationList.setMicro(false);
}
// Start this layout first since it does not require a layout
refs.senchaLogo.animate({dynamic: true, to: {width: new_width}});
// Directly adjust the width config and then run the main wrap container layout
// as the root layout (it and its chidren). This will cause the adjusted size to
// be flushed to the element and animate to that new size.
navigationList.width = new_width;
wrapContainer.updateLayout({isRoot: true});
navigationList.el.addCls('nav-tree-animating');
// We need to switch to micro mode on the navlist *after* the animation (this
// allows the "sweep" to leave the item text in place until it is no longer
// visible.
if (collapsing) {
navigationList.on({
afterlayoutanimation: function () {
navigationList.setMicro(true);
navigationList.el.removeCls('nav-tree-animating');
},
single: true
});
}
}
},
onMainViewRender:function() {
if (!window.location.hash) {
this.redirectTo("dashboard");
}
},
onRouteChange:function(id){
this.setCurrentView(id);
},
onSearchRouteChange: function () {
this.setCurrentView('searchresults');
},
onSwitchToModern: function () {
Ext.Msg.confirm('Switch to Modern', 'Are you sure you want to switch toolkits?',
this.onSwitchToModernConfirmed, this);
},
onSwitchToModernConfirmed: function (choice) {
if (choice === 'yes') {
var s = location.search;
// Strip "?classic" or "&classic" with optionally more "&foo" tokens
// following and ensure we don't start with "?".
s = s.replace(/(^\?|&)classic($|&)/, '').replace(/^\?/, '');
// Add "?modern&" before the remaining tokens and strip & if there are
// none.
location.search = ('?modern&' + s).replace(/&$/, '');
}
},
onAfterRender: function(){
console.log('after render');
}
});
I kinda solve it using "before" action in router with a method that waits for the store to load.
routes: {
':node':{
before: 'wait',
action: 'onRouteChange'
}
and the method:
wait : function() {
var args = Ext.Array.slice(arguments),
action = args.pop(),
store = Ext.getStore('NavigationTree');
if (store.loading) {
store.on('load', action.resume, action);
} else {
action.resume();
}
}
In viewcontroller
//...
var me = this,
refs = me.getReferences(),
mainCard = refs.mainCardPanel,
mainLayout = mainCard.getLayout(),
navigationList = refs.navigationTreeList,
viewModel = me.getViewModel(),
vmData = viewModel.getData(),
store = navigationList.getStore(),
//store = Ext.getStore('NavigationTree'),
node = store.findNode('routeId', hashTag),
view = node ? node.get('view') : null,
lastView = vmData.currentView,
existingItem = mainCard.child('component[routeId=' + hashTag + ']'),
newView;
// BEGIN ADD THIS
if(!view) {
var viewTag = hashTag.charAt(0).toUpperCase() + hashTag.slice(1);
view = hashTag + "." + viewTag;
if(!Ext.ClassManager.getAliasesByName('Fruileg3.view.' + view)) view = '';
}
// END
// Kill any previously routed window
if (lastView && lastView.isWindow) {
lastView.destroy();
}
//...
Related
Actually I'm going to implement a tree view, where the user should have the option to reorder the structure with drag and drop. Actually I can't figure out how to enable drag and drop. I found a lot of examples using the 'treeviewdragdrop' plugin, which is just working with the classic toolkit.
The following Code made me move the first node but not more.
this.toParentSource = new Ext.drag.Source({
element: this.getView().element.down('.x-gridcell'),
constrain: {
element: this.getView().body,
vertical: true
}
});
Can you help me with this problem? I'm using ExtJS 6.5.2 modern toolkit.
This is how I enabled drag and drop for trees in modern Ext JS:
First I've written a plugin which creates the sources that should be draggable.
Plugin
Ext.define('test.component.plugin.TreeDragger', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.treedrag',
mixins: ['Ext.mixin.Observable'],
constructor: function (config) {
this.mixins.observable.constructor.call(this, config);
},
init: function (component) {
var me = this;
this.source = new Ext.drag.Source({
element: component.element,
handle: '.x-gridrow',
constrain: {
element: true,
vertical: true
},
describe: function (info) {
var row = Ext.Component.from(info.eventTarget, component);
info.row = row;
info.record = row.getRecord();
},
proxy: {
type: 'placeholder',
getElement: function (info) {
console.log('proxy: getElement');
var el = Ext.getBody().createChild({
style: 'padding: 10px; width: 100px; border: 1px solid gray; color: red;',
});
el.show().update(info.record.get('description'));
return el;
}
},
// autoDestroy: false,
listeners: {
scope: me,
beforedragstart: me.makeRelayer('beforedragstart'),
dragstart: me.makeRelayer('dragstart'),
dragmove: me.makeRelayer('dragmove'),
dragend: me.makeRelayer('dragend')
}
});
},
disable: function () {
this.source.disable();
},
enable: function () {
this.source.enable();
},
doDestroy: function () {
Ext.destroy(this.source);
this.callParent();
},
makeRelayer: function (name) {
var me = this;
return function (source, info) {
return me.fireEvent(name, me, info);
};
}
});
Next I used this plugin inside my tree.
Tree
xtype: 'tree',
hideHeaders: true,
plugins: {
treedrag: {
type: 'treedrag',
listeners: {
beforedragstart: function (plugin, info) {
// logic to identify the root and prevent it from being moved
console.log('listeners: beforedragstart');
}
}
}
},
columns: [{
xtype: 'treecolumn',
flex: 1,
}
]
Then I defined the drop targets inside the controller.
Controller
afterLoadApportionmentObjectsForTree: function (succes) {
if (succes) {
tree = this.getView().down('tree');
if (tree) {
tree.expandAll();
tree.updateHideHeaders(tree.getHideHeaders());
var store = tree.getStore();
store.remoteFilter = false;
store.filterer = 'bottomup';
this.createDropTargets();
}
}
},
createDropTargets: function () {
var me = this,
rows = tree.innerItems;
Ext.each(rows, function (el) {
var target = new Ext.drag.Target({
element: el.element,
listeners: {
scope: me,
drop: me.onDrop,
beforeDrop: me.onBeforeDrop
}
});
});
},
onDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord(),
parentNode = source.parentNode;
destination.appendChild(source);
destination.expand();
if (!parentNode.hasChildNodes()) {
parentNode.set('leaf', true);
}
},
onBeforeDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord();
// prevent the user to drop the node on itself
// this would lead to an error caused by recursive method calls
if (source == destination) {
return false;
}
// prevent the user to drop a node on it's children
// this would lead to an error caused by recursive method calls
if (source.findChild('number', destination.get('number'), true) != null) {
return false;
}
return true;
}
I'm trying to enable/disable a button when the store getNewRecords() function return the length, but not work!
bind: {
disabled: "{!grid.getStore().getNewRecords().length}"
}
Fiddle: https://fiddle.sencha.com/fiddle/1sj5
Someone have idea to how resolve this?
You need to create a formula in your viewmodel:
viewModel: {
formulas: {
hasNewRecords: function (r) {
return this.getView().down("treepanel").getStore().getNewRecords().length > 0;
}
}
}
then you can use it for your bindings:
bind: {
disabled: "{hasNewRecords}"
}
(probably not the best way to get the data you want).
You can read about it here, here and here .
What you're wanting to do here is currently not possible in the framework. Instead, you should create a ViewModel data value and modify that where need be, like this:
var form = Ext.create("Ext.form.Panel", {
viewModel: {
data: {
newRecords: false
}
},
items: [{
xtype: "textfield",
labelField: "Add Child",
name: "col1",
value: "Teste 123"
}],
tbar: {
xtype: "button",
text: "Add new record",
handler: function () {
var data = this.up("form").getForm().getFieldValues();
var rec = grid.getStore().getAt(0);
data["treeCol"] = rec.childNodes.length + 1;
// setting value, so binding updates
this.lookupViewModel().set('newRecords', true);
rec.appendChild(data);
}
},
bbar: {
xtype: "button",
text: "button to disabled when new records",
bind: {
disabled: "{newRecords}"
}
},
renderTo: Ext.getBody()
});
Or by simply doing this.
In your controller:
me.getView().getViewModel().set('storeLen', store.getNewRecords().length);
In your ViewModel, simply do this:
formulas : {
hasNewRecords : {
get : function(get){
var length = get('storeLen') // --> gets the one you set at your controller
return length > 0 ? true : false;
}
}
}
In your View:
bind : {
disabled : '{hasNewRecords}'
}
Am complete stranger to Extjs. I have a requirement to allow multi select in the Extjs tree. Below piece of code is doing single select. i need to change the code to make it multi select with semi-colon between selected ids. Can anyone please change the code as per the requirement. Also if you could tell me to which field or variable the selected value of the tree is written into. Thanks in advance
Ext.onReady(function() {
Ext.QuickTips.init();
var str='';
var switch_flag = '';
var approvals = '';
var active_mode = '';
var json = null;
Ext.Ajax.request({
url: 'Dmscategorytree/ajax/Message',
method: 'POST',
params:{
lifecycle_id: str,
switch_flag: switch_flag,
approvals: approvals,
active_mode: active_mode
},
success: function(response, options) {
var path='';
var id='';
var text='';
json=response.responseText;
alert(json);
json = json.replace(/"/g,'\"');
json=Ext.util.JSON.decode(json);
var flag=true;
var myloader = new Ext.tree.TreeLoader();
myloader.load = function(node, cb,scope) {
if(this.clearOnLoad){
while(node.firstChild){
node.removeChild(node.firstChild);
}
}
if(this.doPreload(node)){
this.runCallback(cb, scope || node, [node]);
}
cb();
}
var tree = new Ext.tree.TreePanel({
animate:true,
autoScroll:true,
//loader: new Tree.TreeLoader({dataUrl:'get-nodes.php'}),
containerScroll: true,
border: false,
loader:myloader,
rootVisible: false,
listeners:{
checkchange:function(node){
if(flag){
toggleCheck(tree.root,false,node.id);
path=node.attributes.value;
id=node.attributes.ID;
text=node.attributes.text;
path=path.replace(/\^/g,'/');
}
}
}
});
function SelectToParent()
{
try
{
parent.window.opener.callParent(path,id);
parent.window.opener.focus();
parent.window.close();
}
catch(e){
alert('got exception');
window.close();
}
}
function toggleCheck(node,isCheck,nodeId)
{
flag=false;
if(node)
{
var args=[isCheck];
node.cascade(function(){
c=args[0];
if(nodeId!=this.id){
this.ui.toggleCheck(c);
this.attributes.checked=c;
}
},null,args);
}
flag=true;
return true;
}
var root = new Ext.tree.AsyncTreeNode({
text: 'Ext JS',
draggable:false, // disable root node dragging
id:'src',
children: json
});
tree.setRootNode(root);
var topbar = new Ext.Toolbar({
region : 'north',
height:30,
margins: '0 0 0 5',
items:[
{
xtype: 'box',
id: 'title',
autoEl: {
html: '#label.chooseCat#'
}
,width:525
},
{
text: '#label.Done#',
id: 'doneBtn',
tooltip: 'Done',
handler: SelectToParent
}
]
});
topbar.render('tree');
tree.render('tree');
tree.getRootNode().expand();
}
});
});
Please refer the following links,
pass two id s in extjs 4 tree multiSelect and pass one id in single click
Multiselect Tree & Drag and Drop
Drag and Drop between Grid and “multiselect” control in EXTJS
Demo
I'm just trying to create a custom ComboBox to reduce some boilerplate:
Ext.define('App.AutoComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.autocombobox',
states: null,
initComponent: function() {
this.callParent(arguments);
if (!this.states) {
this.queryMode = 'remote';
} else {
this.queryMode = 'local';
this.bindStore(Ext.create('Ext.data.Store', {
type: 'array',
fields: ['_placeholder_'],
data: _.map(this.states, function(state) {
return {_placeholder_ : state}; })
}));
this.displayField = this.valueField = '_placeholder_'
}
this.validator = function(v) {
var field = this.displayField,
index = this.getStore().findExact(field, v);
return (index!==-1) ? true : 'Invalid selection';
};
},
listeners: {
select: function(combo, records) {
console.log(combo.getStore().indexOf(records[0])); // !== -1
}
}
});
So that I can use it like:
requires: ['App.AutoComboBox'],
...
items: [{
xtype: 'autocombobox',
name: 'test_local',
fieldLabel: 'test_local',
states: [ 'cat', 'dog' ] // local
}, {
xtype: 'autocombobox',
name: 'test_remote',
fieldLabel: 'test_remote',
store: 'Chipmunks', // a remote store
displayField: 'chipmunk_name'
}]
...
But something is amiss. The AutoComboBox renders OK, shows dropdown of records fine, but when I select an item from the dropdown, the combobox's display field is not set. The store seems to find the selected record (as seen by the select listener), but the value is still not set...
Help? thanks.
Edit: FIXED by moving this.callParent(arguments) after the new store is bound. Now accepting answers that explain why this fixes it... (I don't know why it works.. but it does)
In the parent initComponent method, the displayField is used to create the displayTpl:
if (!me.displayTpl) {
me.displayTpl = new Ext.XTemplate(
'<tpl for=".">' +
'{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' +
'<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
'</tpl>'
);
} else if (Ext.isString(me.displayTpl)) {
me.displayTpl = new Ext.XTemplate(me.displayTpl);
}
The bindStore call has probably nothing to do with it, I believe that this is this line that must be put before the call to the parent method:
this.displayField = this.valueField = '_placeholder_';
The goal is to use buffered store for the dynamic data set.
The workflow is below:
Some data is already present on server.
Clients uses buffered store & infinite grid to handle the data.
When the application runs the store is loading
and 'load' event scrolls the grid to the last message.
Some records are added to server.
Client gets a push notification and runs store reload.
topic.store.load({addRecords: true});
The load event runs and tries to scroll to the last message again but failes:
TypeError: offsetsTo is null
e = Ext.fly(offsetsTo.el || offsetsTo, '_internal').getXY();
Seems that the grid view doesn't refreshes and doesn't show the added records, only the white spaces on their places.
Any ideas how can I make the grid view refresh correctly?
The store initialization:
Ext.define('orm.data.Store', {
extend: 'Ext.data.Store',
requires: ['orm.data.writer.Writer'],
constructor: function (config) {
Ext.apply(this, config);
this.proxy = Ext.merge(this.proxy, {
type: 'rest',
batchActions: true,
reader: {
type: 'json',
root: 'rows'
},
writer: {
type: 'orm'
}
});
this.callParent(arguments);
}
});
Ext.define('akma.chat.model.ChatMessage', {
extend:'Ext.data.Model',
fields:[
{ name:'id', type:'int', defaultValue : undefined },
{ name:'createDate', type:'date', dateFormat:'Y-m-d\\TH:i:s', defaultValue : undefined },
{ name:'creator', type:'User', isManyToOne : true, defaultValue : undefined },
{ name:'message', type:'string', defaultValue : undefined },
{ name:'nameFrom', type:'string', defaultValue : undefined },
{ name:'topic', type:'Topic', isManyToOne : true, defaultValue : undefined }
],
idProperty: 'id'
});
Ext.define('akma.chat.store.ChatMessages', {
extend: 'orm.data.Store',
requires: ['orm.data.Store'],
alias: 'store.akma.chat.store.ChatMessages',
storeId: 'ChatMessages',
model: 'akma.chat.model.ChatMessage',
proxy: {
url: 'http://localhost:8080/chat/services/entities/chatmessage'
}
});
var store = Ext.create('akma.chat.store.ChatMessages', {
buffered: true,
pageSize: 10,
trailingBufferZone: 5,
leadingBufferZone: 5,
purgePageCount: 0,
scrollToLoadBuffer: 10,
autoLoad: false,
sorters: [
{
property: 'id',
direction: 'ASC'
}
]
});
Grid initialization:
Ext.define('akma.chat.view.TopicGrid', {
alias: 'widget.akma.chat.view.TopicGrid',
extend: 'akma.chat.view.grid.DefaultChatMessageGrid',
requires: ['akma.chat.Chat', 'akma.UIUtils', 'Ext.grid.plugin.BufferedRenderer'],
features: [],
hasPagingBar: false,
height: 500,
loadedMsg: 0,
currentPage: 0,
oldId: undefined,
forceFit: true,
itemId: 'topicGrid',
selModel: {
pruneRemoved: false
},
multiSelect: true,
viewConfig: {
trackOver: false
},
plugins: [{
ptype: 'bufferedrenderer',
pluginId: 'bufferedrenderer',
variableRowHeight: true,
trailingBufferZone: 5,
leadingBufferZone: 5,
scrollToLoadBuffer: 10
}],
tbar: [{
text: 'unmask',
handler: function(){
this.up('#topicGrid').getView().loadMask.hide();
}
}],
constructor: function (config) {
this.topicId = config.topicId;
this.store = akma.chat.Chat.getMessageStoreInstance(this.topicId);
this.topic = akma.chat.Chat.getTopic(this.topicId);
var topicPanel = this;
this.store.on('load', function (store, records) {
var loadedMsg = store.getTotalCount();
var pageSize = store.pageSize;
store.currentPage = Math.ceil(loadedMsg/pageSize);
if (records && records.length > 0) {
var newId = records[0].data.id;
if (topicPanel.oldId) {
var element;
for (var i = topicPanel.oldId; i < newId; i++) {
element = Ext.get(i + '');
topicPanel.blinkMessage(element);
}
}
topicPanel.oldId = records[records.length-1].data.id;
var view = topicPanel.getView();
view.refresh();
topicPanel.getPlugin('bufferedrenderer').scrollTo(store.getTotalCount()-1);
}
});
this.callParent(arguments);
this.on('afterrender', function (grid) {
grid.getStore().load();
});
var me = this;
akma.UIUtils.onPasteArray.push(function (e, it) {
if(e.clipboardData){
var items = e.clipboardData.items;
for (var i = 0; i < items.length; ++i) {
if (items[i].kind == 'file' && items[i].type.indexOf('image/') !== -1) {
var blob = items[i].getAsFile();
akma.chat.Chat.upload(blob, function (event) {
var response = Ext.JSON.decode(event.target.responseText);
var fileId = response.rows[0].id;
me.sendMessage('<img src="/chat/services/file?id=' + fileId + '" />');
})
}
}
}
});
akma.UIUtils.addOnPasteListener();
},
sendMessage: function(message){
if(message){
var topicGrid = this;
Ext.Ajax.request({
method: 'POST',
url: topicGrid.store.proxy.url,
params:{
rows: Ext.encode([{"message":message,"topic":{"id":topicGrid.topicId}}])
}
});
}
},
blinkMessage: function (messageElement) {
if (messageElement) {
var blinking = setInterval(function () {
messageElement.removeCls('red');
messageElement.addCls('yellow');
setTimeout(function () {
messageElement.addCls('red');
messageElement.removeCls('yellow');
}, 250)
}, 500);
setTimeout(function () {
clearInterval(blinking);
messageElement.addCls('red');
messageElement.removeCls('yellow');
}, this.showInterval ? this.showInterval : 3000)
}
},
columns: [ {
dataIndex: 'message',
text: 'Message',
renderer: function (value, p, record) {
var firstSpan = "<span id='" + record.data.id + "'>";
var creator = record.data.creator;
return Ext.String.format('<div style="white-space:normal !important;">{3}{1} : {0}{4}</div>',
value,
creator ? '<span style="color: #' + creator.chatColor + ';">' + creator.username + '</span>' : 'N/A',
record.data.id,
firstSpan,
'</span>'
);
}
}
]
});
upd: Seems that the problem is not in View. The bufferedrenderer plugin ties to scroll to the record.
It runs a callback function:
callback: function(range, start, end) {
me.renderRange(start, end, true);
targetRec = store.data.getRange(recordIdx, recordIdx)[0];
.....
store.data.getRange(recordIdx, recordIdx)[0]
tries to get the last record in the store.
....
Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
getPage returns all records of the given page, but the last record is missing i.e. the store was not updated perfectly.
Any ideas how to fix?
The problem is that store.load() doesn't fill up store PageMap with the new data. The simplest fix is using store.reload() instead.
Maybe you are to early when listening to the load event. I am doing roughly the same in my application (not scrolling to the end, but to some arbitrary record after load). I do the view-refresh and bufferedrender-scrollTo in the callback of the store.load().
Given your code this would look like:
this.on('afterrender', function (grid) {
var store = grid.getStore();
store.load({
callback: function {
// snip
var view = topicPanel.getView();
view.refresh();
topicPanel.getPlugin('bufferedrenderer').scrollTo(store.getTotalCount()-1);
}
});
});