Stateful selection in Ext JS - javascript

I have a regular tree with elements. What i want to do is following:
When I reload the page, the selected item must be the same as before ( I select only 1 item in the tree).
For example, when I click on 'Sue Potato' - it is selected and when I refresh the page, it must look the same (be also selected).
I've tried reading some Stateful, Provider, Manager on the Sencha Docs, but I didn't get it.
Controller code:
Ext.define('FirstApp.controller.Main', {
extend: 'Ext.app.Controller',
refs: [
{
ref: 'grid',
selector: 'lesson-grid'
},
{
ref: 'tree',
selector: 'school-tree'
}
],
init: function() {
Ext.state.Manager.setProvider(Ext.create('Ext.state.LocalStorageProvider'));
}
});
Tree code:
Ext.define('FirstApp.view.SchoolTree', {
extend: 'Ext.tree.Panel',
xtype: 'school-tree',
stateful: true,
stateId: 'stateGrid',
stateEvents:['selection'],
constructor: function() {
var that = this;
this.store = Ext.create('FirstApp.store.School');
this.store.on('load', function () {
that.getSelectionModel().select(1, true);
});
this.callParent(arguments);
this.getState = function() {
return that.getSelectionModel().getSelection();
};
this.applyState = function() {
};
}
});
Help would be much appreciated.

This is a working code of the requirement above.I had to get an id of the selected element and then pass it to applyState.
Ext.define('FirstApp.view.SchoolTree', {
extend: 'Ext.tree.Panel',
xtype: 'school-tree',
stateful: true,
stateId: 'stateTree',
stateEvents:['selectionchange'],
constructor: function() {
var that = this;
this.store = Ext.create('FirstApp.store.School');
this.store.on('load', function () {
that.getSelectionModel().select(1);
});
this.callParent(arguments);
},
getState: function() {
return {
'stateTree': this.getSelectionModel().getSelection()[0].getId()
};
},
applyState: function(state) {
var me = this;
this.store.on('load', function(record) {
record = this.getById(state.stateTree);
me.getSelectionModel().select(record);
});
}
});

You stored tree state and set it into State Manager
Ext.state.Manager.setProvider(Ext.create('Ext.state.LocalStorageProvider'));
So when you will refresh your page state will apply and an item will be also selected.
Is there any requirement to store state of tree? If yes and still you don't want to selected then you can forcefully clear selection on tree render.

Related

Problem with variable scope in nested Backbone views

I have a parent view which contains a Backgrid view. In the parent view's initialize section I define a variable isWellSelected. The variable is toggled in the Backgrid column logic when a tickbox is checked. I am able to watch the variable toggled when a box is ticked and unticked.
However, once an event fires the variable is no longer in scope for the event to see. I suspect I may need to pass the variable to the Backrgrid view but I am unsure how to do that correctly. Please advise.
app.wellCollectionView = Backbone.View.extend({
template: _.template($('#wellTemplate').html()),
initialize: function() {
this.isWellSelected = false;
// isWellSelected toggled to true when a tickbox is checked in the columns block.
var columns = [...];
// instantiate collection
var wellCollection = new app.wellCollection;
// Set up a grid view to use the pageable collection
var wellGrid = new Backgrid.Grid({
columns: columns,
collection: wellCollection
});
// Initialize the paginator
var paginator = new Backgrid.Extension.Paginator({
collection: wellCollection
});
// Render the template
this.$el.html(this.template());
// Render the grid
this.$el.append(wellGrid.render().el);
this.$el.append(paginator.render().$el);
wellCollection.fetch({reset: true}).then(function () {...});
},
events: {
'click #EvaluateWell': function(){
this.evalWell(event, this.isWellSelected);
console.log("In events - isWellSelected: " + this.isWellSelected);}
},
// More stuff
}
Constructive feedback welcome.
Thanks!
Adding a snippet for "columns" as per JT's request:
var columns = [
{
name: '',
label: 'Select',
cell: Backgrid.BooleanCell.extend({
events : {
'change': function(ev){
var $checkbox = $(ev.target);
var $checkboxes = $('.backgrid input[type=checkbox]');
if($checkbox.is(':checked')) {
$checkboxes.attr("disabled", true);
this.isWellSelected = true;
// Disable all checkboxes but this one
$checkbox.removeAttr("disabled");
} else {
// Enable all checkboxes again
$checkboxes.removeAttr("disabled");
this.isWellSelected = false;
}
}
}
})
}, {
name: "api",
label: "API",
editable: false, // Display only!
cell: "string"
}, {
name: "company",
label: "Operator",
editable: false, // Display only!
cell: "string"
}];

Trigger function in moment column is sorted in GridX

I use SingleSort module with Gridx. I can't find (I already checked SingleSort documentation and found nothing) a way to react on sort event. I need info about which (and how) column is sorted. I know how get info about sorting (getSortData method) but i don't know how make react in moment sort is made. I can't made onRender-event function because after sorting i will send that info to webapi get new data and again render Grid so event will be triggered again.
define([
"dojo/Evented",
"dojo/_base/declare",
"dojo/store/Memory",
"gridx/core/model/cache/Sync",
"gridx/Grid",
"gridx/modules/ColumnResizer",
'gridx/modules/Focus',
'gridx/modules/RowHeader',
'gridx/modules/select/Row',
'gridx/modules/select/Column',
'gridx/modules/select/Cell',
"gridx/modules/SingleSort"
], function (Evented, declare, Memory, Cache, Grid, ColumnResizer, Focus, RowHeader, SelectRow, SelectColumn, SelectCell, Sort) {
return declare([Evented], {
_counter: 1,
_topOffset: 100,
constructor: function () {
},
initialize: function () {
this._initGrid();
this._resizeGridContainer();
},
clear: function () {
var store = new Memory({ data: [] });
this._grid.setStore(store);
this._counter = 1;
},
_initGrid: function () {
var _this = this;
var store = new Memory({
data: []
});
var structure = [
{
id: 'operationType', field: 'operationType', name: dojo.byId(this._operationTypeId).value
},
{
id: 'transportationType', field: 'transportationType', name: dojo.byId(this._transportationUnitTypeId).value
},
{
id: 'transportationTypeLength', field: 'transportationTypeLength', name: dojo.byId(this._transportationLengthId).value
}
]
this._grid = Grid({
id: this._gridId,
cacheClass: Cache,
store: store,
structure: structure,
modules: [
ColumnResizer,
Sort
],
selectRowTriggerOnCell: true
});
this._grid.placeAt(this._gridPlaceholderId);
this._grid.startup();
},
_resizeGridContainer: function () {
var _this = this;
var container = dojo.byId(this._gridContainerId);
var height = container.parentElement.clientHeight - this._topOffset;
require(["dojo/dom-style"], function (domStyle) {
domStyle.set(_this._gridContainerId, "height", height + "px");
})
},
});
});
you need to tap on to the gridx SingleSort module sort method like this
this._grid.connect( this._grid.Sort, 'sort', function(colId, isDescending, skipUpdateBody){
alert(colId+" "+isDescending+" "+skipUpdateBody);
});
this will trigger the event whenever a grid column is sorted. Hope this helps.

ExtJS 5: Initial ViewModel data not firing formula

I have a ViewModel that contains some initial data... this initial data is based off of a global variable that I have created. In the ViewModel, I have a formula that does some logic based on the data set from the global variable. The interesting thing is, this formula does not fire when the ViewModel is created. I'm assuming this is because the Something.Test property does not exist, so the ViewModel internals have some smarts to not fire the method if that property does not exist.
If the property doesn't exist, how do I fire the formula anyway? I know I could look for Something check to see if it has the property Test, but I'm curious why this example wouldn't work. Here's the example:
Ext.application({
name : 'Fiddle',
launch : function() {
// Define global var Something
Ext.define('Something', {
singleton: true
});
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myView',
data: {
Something: window.Something
},
formulas: {
testSomething: function(getter) {
console.log('here', getter('Something.Test'));
return getter('Something.Test');
},
myTitle: function(getter) {
return 'My Title';
}
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
bind: {
title: '{myTitle}'
},
viewModel: {
type: 'myView'
}
});
var view = Ext.create('MyView', {
renderTo: Ext.getBody()
});
// This will fire the ViewModel formula
//view.getViewModel().set('Something', window.Something);
console.log(Something, window.Something)
}
});
You can workout some logic to handle when Something.Test is not available, something like:
data: {
Something: window.Something && window.Something.Test || {Test: null}
},
formulas: {
testSomething: function(get) {
var val = get('Something.Test');
console.log('Test');
return val;
},
myTitle: function(getter) {
return 'My Title';
}
}

EXTJS add fireEvent to dynamically created elements using tpl

I have an array of objects that I apply to html using new Ext.XTemplate and then I use the result as my html to a panel. When I click the button everything is fine using delegate.
Question:
How can I add events to the dynamically added buttons, so that I can use them in a controller?
initComponent: function() {
var me = this;
var data = [{
caption: 'Dashboard',
myclass: 'dashboard'
}, {
caption: 'clients',
myclass: 'clients'
}];
var myTpl = new Ext.XTemplate(
'<table><tr>',
'<tpl for="."><td>',
'<div style="width:200px;background-color:#004544;" class="thumb-wrap">',
'<button style="color:#fff;cursor:pointer;" class="{myclass}">{caption}</button>',
'</div></td>',
'</tpl></tr></table>');
var htmlApplied = myTpl.apply(data);
this.items = [{
xtype: 'panel',
html: htmlApplied,
scope: me,
listeners: {
el: {
delegate: 'button',
click: function(clickcmp, button) {
console.log(clickcmp);
console.log(button);
switch (button.className) {
case 'dashboard':
this.fireEvent('menuload');
break;
case 'clients':
//alert('clients');
break;
}
}
}
}
}]
this.addEvents({
menuload: true
});
}
Controller
Ext.define("Something.Control", {
extend: 'Ext.app.Controller',
refs: [],
init: function() {
this.control({
'selector': {
menuload: this.activateMenu
}
});
},
activateMenu: function() {
}
});
Assuming selector matches the component code that you added above, what you're missing is the scope on the listener. By default, the scope will default to the inner panel. The scope: me needs to go down inside the el block:
el: {
scope: this,
delegate: 'div'
}

Backbone: restore focus after rendering

I've got a little problem here:
In my Backbone.js app I save changes in a content editable on blur. This means, that when pressing the tab key the whole view is re-rendered and I loose the focus on the next element. How can I restore this?
You can maintain a property, either in the view (as a plain attribute, as in the example below) or model, to store the currently focused element. Whenever focus changes, update the property.
After re-rendering stuff, set the focus to the element manually.
Here is a minimal code:
var myView = Backbone.View.extend({
el: $('#formElement'),
initialize: function() {
_.bindAll(this);
}
events: {
'focus input': "updateFocus"
},
updateFocus: function(event) {
this.focusedElem = $(event.target);
},
render: function() {
// After rendering is complete
this.focusedElem.focus();
}
});
I use a dedicated ViewModel and View for every input. It has a special readValue/writeValue methods which update element instead of recreating it. It looks this way:
var TextInput = Backbone.Model.extend({ // abstract
defaults: {
value: '', // text
visible: true, // determines if input element is visible
readonly: false, // determines if input element is read only
enabled: true, // determines if input element is enabled
delay: 750 // view/model sync latency
}
});
var TextInputView = Backbone.View.extend({
template: _.template($('#text-input').html()),
initialize: function (options) {
this.model.bind('change:visible', this.render, this);
this.model.bind('change:readonly', this.render, this);
this.model.bind('change:enabled', this.render, this);
this.model.bind('change:value', this.readValue, this);
},
events: {
'change input': 'writeValue',
'keyup input': 'writeValue'
},
render: function () {
$(this.el).html(this.template(this.model))
.find('input')
.prop({
readonly: this.model.get('readonly'),
disabled: !this.model.get('enabled')
})
.toggleClass('hidden', !this.model.get('visible'));
this.readValue();
return this;
},
changeTimer: null,
writeValue: function () {
if (this.changeTimer)
clearTimeout(this.changeTimer);
var that = this;
this.changeTimer = setTimeout(function () {
that.model.set({ value: that.$('input').val() });
}, this.model.get('delay'));
},
readValue: function () {
if (this.$('input').val() != this.model.get('value'))
this.$('input').val(this.model.get('value'));
}
});
I found that I wanted it to go to the "next" element after rendering. Also, you can't remember an element in JQuery that gets removed from the DOM. So I record the name of the input instead of the input itself. Combining the previous answers you can do something similar to below. Remember I have some assumptions in there, like names on the inputs and that I search within the fieldset.
getNextInputForName = function(desiredName) {
var desiredElement = false;
var foundElement;
$("fieldset input").each(function(index) {
if (desiredElement) {
foundElement = $(this);
return false;
}
if ($(this).attr("name") === desiredName) {
desiredElement = true;
}
});
return foundElement;
}
var myView = Backbone.View.extend({
el: $('#formElement'),
initialize: function() {
_.bindAll(this);
}
events: {
'focus input': "updateFocus"
},
updateFocus: function(event) {
this.focusedElem = $(event.target).attr("name");
},
render: function() {
// After rendering is complete
if( this.focusedElem ) {
getNextInputForName(this.focusedElem).focus();
}
}
});

Categories

Resources