ExtJS 5 - Dynamically add items to split button menu from store - javascript

I want to associate a store to split button menu. I am using extjs version 5. I have searched on the web and even went through sencha documentation but I couldn't figure out a way to achieve this.
Currently i'm holding the menu details in variable and assigning it to the splitbutton xtyoe menu property.I want to achieve the same using store, your help is much appreciated!
Program Code:-
var menuJSON = [{
text:'Menu1',
menu:[{text:'Submenu1'},{text:'Submenu2'}]
},{
text:'Menu2',
menu:[{text:'Submenu1'},{text:'Submenu2'}]
},{
text:'Menu3',
menu:[{text:'Submenu1'},{text:'Submenu2'}]
},{
text:'Menu4'
},{
text:'Menu5',
menu:[{text:'Submenu1'},{text:'Submenu2'}]
}];
{
region: 'south',
fbar: [{
xtype:'splitbutton',
id: 'app-starterMenu',
text:'Start',
scale:'small',
iconCls:'icon-start',
menuAlign: 'bl-tl',
menu: menuJSON
}]
}
Thanks in advance!

It would be helpful if you can provide example code for the store or relative controller, however this is still easier than you might think. Of course, it's nothing that comes out of the box, but it's a perfect case of using listeners.
Whenever the store changes data, you want to update your button configuration. Therefore just add the relevant listener to your store - maybe using the refresh or update event (depending on your specific use case). Then whenever the store data changes, you need to grab a reference to your button (or associated menu) and update the items. In it's most simplistic form, an example might be as follows:
store: {
listeners: {
refresh: function(store) {
// Get all the raw data from records and use it to set items on the menu
var rawData = Ext.Array.pluck(store.getRange(),'data');
Ext.getCmp('app-starterMenu').setMenu({
items: rawData
});
// *OR* Loop through the store data conditionally and include what you need
Ext.getCmp('app-starterMenu').getMenu().removeAll();
store.each(function(record){
Ext.getCmp('app-starterMenu').getMenu().add(record.getData());
});
}
}
}

Related

Handling updates on nested screens using Context and React Navigation in React Native

Let's say I am building a shopping list app. I have the ability to create different shopping lists. I have three screens:
-- Shopping Lists: displays all the lists
---- List Details: displays all the items from a list
------ List Item Details: displays all the info about an item
To store the state and avoid prop drilling I use Context. My state could look like:
shoppingLists = [
{
title: 'Groceries',
items: [{
name: 'Apples',
quantity: 3
... (other info)
},
items: [{
name: 'Oranges',
quantity: 6,
...
}]
},
{
title: 'Office Supplies',
items: [{
name: 'Paper',
quantity: 2
... (other info)
},
items: [{
name: 'Pens',
quantity: 25,
...
}]
}]
When I tap on a Shopping List on the Shopping Lists screen I do
navigation.navigate('ListDetails', {params: listItem})
In the List Details screen I have de ability to change the quantity of an item or delete them. If I do any of this actions I have to make an API call to my server to update the value on the database. Here are my questions:
Currently I store the values in a local variable in my screen for example:
const [title, setTitle] = useState(props.route.params.title)
const [items, setItems] = useState(prop.route.params.items)
And if I make a change in the quantity I use setState to update my local array and then make an API call to update the context. This results problematic when adding more nested screens.
For example if I would give the user the ability to change the item quantity in the List Item Details screen, when the user goes back to the List Details screen, the values would not be updated.
My question is, which is the correct way to grab the state of the context and update it locally?
For example should I do something like:
navigation.navigate('ListDetails', {params: { listName: 'Groceries' })
And then in my List Details screen grab the correct list from the array like:
const {lists} = useContext(ShoppingLists)
const list = lists.filter(l => l.title === props.route.params.title)
What is the correct way?
The other question I have is a more general question. I want to use optimistic responses: when the user updates the quantity of an item in the list I update it locally and send the request to the server. If there's an error rollback the change. Which would be the correct way of doing this?
Thanks!
I would advise against updating context data in such a way. You could create a setList() method in the context instead, or updateList() to aggregate new data to the existing data.
This is related to the previous one. You could wrap the logic of POSTing to the server in a try-catch block and throw an error when it's not successful. Then you can set the new value only when it's successful. Another option would be to return the promise, so you could handle errors from outside, like setList().then(response => {}).catch(error => {}) though I don't think this looks that good.
Think you're over complicating it and also in your list you're not showing an id for each item. You should build your database with all the items and their unique id then call upon that when needed. The id should never change and that's easier to pass around and reference.
For example you can build an object in context with just the id and quantity and use useReducer for this:
https://reactjs.org/docs/hooks-reference.html#usereducer

Extjs multiple representations of data

I have a Rest endpoint, that sends and receives objects of the form
[
{id: 1, Name: "Type"},
{id: 2, Name: "Type:Subtype"},
...
]
I want to display this in an editable tree, using Sencha Extjs 6. I am confused as to where and when to transform the data, and how to keep changes synchronized without side effects. My current (not nice) method is to reload the data and then reset the tree's store using the converted values, but that collapses all of the expanded nodes
I can get and save entries using a model and a store
I can convert the data into a form suitable for use in a treepanel
I do not know the "right" way to do so, and to have changes in either store reflected in the other.
For clarity, the converted tree store has a data stucture:
[
{
text: "Type",
children: [
{
text: "Subtype",
isLeaf: true
}
]
}
]
Came up with the answer: a save button.
Bound the Load event of the Rest Store to the controller, and in that method copied the values into the TreeStore.
Edits are made to the TreeStore - kept purely client side
Save button copies new data back into Rest Store and calls sync(). Easy to follow. Code is kept mostly declarative. And I can provide default values in the VM's data field. Easy to do - shame it's not made clear in the documentation somewhere

ExtJS How to update filtered store data?

Just doing these steps:
I have grid filled with data from store.
I filter it (for example: Show only with status "late").
Data in store is updated.
I still see filtered old data (old records with status late).
I remove filter, All new data appear, with all old records which were not visible during filter.
Maybe someone know why and how to fix this?
FIXED
This code in store made a trick:
listeners : {
beforeload : function() {
this.data.clear();
if(this.data._source)
this.data._source.clear();
}
},
You can bind yourStore.reload() to your update event.

How would I add a query filter to the Iteration Tracking Board?

So I have the source code for the Iteration Tracking Board on Rally. All I want to do is to add a query filter that is similar to the Portfolio Hierarchy app or the Portfolio Kanban Board.
If this is possible, I think that I may need to add it in the javascript code as a plugin and I was wondering how that should be coded.
Is this correct? Or can I not even add the filter as a plugin because it is not defined as one in Rally?
For some quick background, here is a guide on working with settings in apps: https://help.rallydev.com/apps/2.0rc2/doc/#!/guide/settings
This is a 2 parter. First, you'll need to add the query settings field to your app. Since this field is commonly used across apps there is a handy preconfigured on you can just reference by type:
getSettingsFields: function() {
var fields = this.callParent(arguments);
//...
//existing code omitted for brevity
//...
fields.push({type: 'query'});
return fields;
}
Then you'll need to actually use that setting to filter the data shown. Add the following to the cardBoardConfig object:
storeConfig: {
filters: this.getSetting('query') ?
[Rally.data.QueryFilter.fromQueryString(this.getSetting('query'))] : []
}

Edit List using Backbone.js/Marionette

I'll start off with I'm new to these two frameworks but I'm really excited with what I've learned so far with them. Big improvement with the way I've been doing things.
I want to display a collection of items (trip itineraries) in say a table. I only want to display a couple of the itinerary fields in the table because there are many fields. When you edit/add an item, I would like to display a form to edit all the fields in a different region or in a modal and obviously when you save the list/table is updated. I'm unsure of the best way to structure this and am hoping someone could point me in the right direction or even better an example of something similar. My searches so far have came up short. It seems I should use a composite view for the list but how to best pass the selected item off to be edited is where I'm kinda stuck at. Any pointers would be much appreciated.
I'd use a CompositeView for the table, and an ItemView for the form. Clearly this is incomplete, but it should get you started. Actually... I kind of got carried away.
What follows are some ideas for the basic structure & flow. The implementation details, including templates, I'll leave to you.
The table/row views:
// each row in the table
var RowView = Backbone.Marionette.ItemView.extend({
tagName: "tr"
});
// This could be a table tag itself, or a container which holds the table.
// You want to use a CompositeView rather than a CollectionView so you can
// render the containing template (the table tag, headers), and also, it works
// if you have an actual collection model (ItineraryList, etc).
var TableView = Backbone.Marionette.CompositeView.extend({
itemView: RowView,
collectionEvents: {
"change": "render"
}
});
The form view:
// This would be a simple form wrapper that pulls default values from
// its model. There are some plugins in this space to help with
// forms in backbone (e.g. backbone-forms), but they may or may not
// be worth the bloat, or might be tricky to work with Marionette.
var FormView = Backbone.Marionette.ItemView.extend({
events: {
"submit form": "onFormSubmit"
},
data: function () {
// here you'd need to extract your form data, using `serializeArray`
// or some such, or look into a form plugin.
return {};
},
// Clearly this is incomplete. You'd need to handle errors,
// perhaps validation, etc. You probably also want to bind to
// collection:change or something to close the formview on success.
//
// Re-rendering the table should be handled by the collection change
// event handler on the table view
onFormSubmit: function (e) {
e.preventDefault();
if (this.model.isNew()) {
this.collection.create(this.data());
} else {
this.model.save(this.data());
}
}
});
Somewhere in your load process you'd instantiate a collection and show it:
// first off, somewhere on page load you'd instantiate and probably fetch
// a collection, and kick off the tableview
var itineraries = new Itineraries();
itineraries.fetch();
// For simplicity I'm assuming your app has two regions, form and table,
// you'll probably want to change this.
app.table.show(new TableView({collection: itineraries}));
Making routes for the edit and new itinerary links makes sense, but if your form is in a modal you might want to bind to button clicks instead. Either way, you'd open the form something like this:
// in the router (/itineraries/edit/:id) or a click handler...
function editItinerary(id) {
var itinerary = itineraries.get(id);
// then show a view, however you do that. If you're using a typical
// marionette region pattern it might look like
app.form.show(new FormView({
collection: itineraries,
model: itinerary
});
}
// once again, a route (/itineraries/new), or a click handler...
function newItinerary() {
app.form.show(new FormView({
collection: itineraries,
model: new Itinerary()
}));
}

Categories