Form field to pick from a list of objects in a store - forms

I'm new to Sencha, trying to implement a Sencha formpanel with a field to pick from a list of objects in a store. This seems like a sufficiently basic pattern that there must be a simple solution. I have a model which defines an ajax endpoint returning a json array (places) of name/id pairs:
Ext.define('MyApp.model.Place', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'name', type: 'string'},
{name: 'id', type: 'int'},
],
proxy: {
type: 'ajax',
url: '/m/places/',
reader: {
type: 'json',
rootProperty: 'places',
},
},
},
});
And a store:
Ext.define('MyApp.store.Place', {
extend: 'Ext.data.Store',
requires: ['MyApp.model.Place'],
config: {
model: 'MyApp.model.Place',
autoLoad: true,
sorters: [
{
property : 'name',
direction: 'ASC',
},
],
},
});
I want a read-only text field for the place name and a hidden field to submit the place id:
{
xtype: 'textfield',
name: 'place_name',
label: 'Place',
readOnly: true,
listeners: {
focus: function () {
var place_picker = Ext.create('Ext.Picker', {
slots: [
{
name: 'place_name',
title: 'Choose a Place',
store: Ext.create('MyApp.store.Place'),
itemTpl: '{name}',
listeners: {
itemtap: function (obj, index, target, record, e, eOpts) {
var form = Ext.getCmp('entityform');
form.setValues({
place_name: record.get('name'),
place_id: record.get('id'),
});
// how to dismiss the picker?
obj.parent.hide();
},
},
},
]
});
Ext.Viewport.add(place_picker);
place_picker.show();
},
},
},
{
xtype: 'hiddenfield',
name: 'place_id',
},
At this point, tapping the field causes the picker to slide up from the bottom and display the loading animation, but it is not being populated with the list of place names, although I can see that the ajax request has been made and that it has returned the expected json document.
I'll stop here and ask if I'm on the right track or is there some better approach out there?
Why isn't the picker being populated with the results of the ajax request? Is my use of itemTpl incorrect?
Am I setting the form fields in a sencha-appropriate way?
How should I dismiss the picker on itemtap? I somehow doubt that my use of hide() is the proper way.

Picker slots has an data config which is an array of objects. It should have a specific format with text and value.
slots :[
data : [{
text : someValue,
value : someValue1,}] ]
You need to add objects which has the fields text and value to slots.

Related

ExtJS 7.2 - Loading record into a form with chained comboboxes and forceSelection:true does not load all values

I have a form with two chained comboboxes. The first chained combobox dictates the values that appear in the second. I have forceSelection:true on the second combobox so that when a user changes the first combo, the second will be set blank because it no longer will be a valid option. The chained combos function as expected but when I load a record into this form using getForm().loadRecord(record) the value for the first combo is set properly but the second is not unless I set forceSelection:false.
The following fiddle should make things pretty clear: sencha fiddle
Clicking "Load Record" should load in Fruits/Apple, but only Fruits is shown. Clicking "Load Record" a second time achieves the desired result. If you comment out forceSelection: true it works as expected but then the chained combos don't function as desired. Is there anything I'm doing wrong here?
It is not so easy. What is happening when you are running form.loadRecord(rec).
you set the FoodGroup combobox
you set the FoodName combobox. BUT the value is not there, because your filter did not switch to appropriate food groups. That is why commenting force selection helps. (That is why commenting filter also help).
turned on the filter of food names. Store now have new values.
You are clicking the button second time. The first combobox value is the same, filters are not trigged (triggered?), you already have appropriate values in the second store and the value is selected.
How to fix:
The fix is ugly. You can listen on store 'refresh' (it means the filters are triggered) and then set the second value (or set the values again).
Ext.define('Fiddle.view.FormModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.fiddle-form-model',
stores: {
foodgroups: {
fields: ['name'],
data: [{
foodgroupname: 'Fruits'
}, {
foodgroupname: 'Vegetables'
}]
},
foods: {
fields: ['foodgroupname', 'foodname'],
filters: {
property: 'foodgroupname',
value: '{selectedFoodgroup.foodgroupname}'
},
data: [{
foodname: 'Apple',
foodgroupname: 'Fruits'
}, {
foodname: 'Banana',
foodgroupname: 'Fruits'
}, {
foodname: 'Lettuce',
foodgroupname: 'Vegetables'
}, {
foodname: 'Carrot',
foodgroupname: 'Vegetables'
}]
}
}
});
Ext.define('Fiddle.view.Form', {
extend: 'Ext.form.Panel',
xtype: 'fiddle-form',
viewModel: {
type: 'fiddle-form-model'
},
title: 'Combos',
items: [{
xtype: 'combo',
itemId: 'FoodGroup', // To access FoodGroup
displayField: 'foodgroupname',
bind: {
store: '{foodgroups}',
selection: '{selectedFoodgroup}'
},
valueField: 'foodgroupname',
forceSelection: true,
name: 'foodgroup',
fieldLabel: 'Food Group',
value: 'Vegetables'
}, {
xtype: 'combo',
itemId: 'FoodName', // To access FoodName
bind: {
store: '{foods}'
},
queryMode: 'local',
forceSelection: true, //COMMENTING THIS OUT ACHIEVES DESIRED LOAD RECORD BEHAVIOR
displayField: 'foodname',
valueField: 'foodname',
name: 'food',
fieldLabel: 'Food',
value: 'Carrot'
}],
buttons: [{
text: 'Load Record',
handler: function (btn) {
// OUR UGLY FIX
var form = btn.up('form'),
foodGroupComboBox = form.down('combobox#FoodGroup'),
foodNameComboBox = form.down('combobox#FoodName'),
record = Ext.create('Ext.data.Model', {
foodgroup: 'Fruits',
food: 'Apple'
});
foodNameComboBox.getStore().on('refresh', function (store) {
form.loadRecord(record);
}, this, {
single: true
})
form.loadRecord(record);
}
}]
});
Ext.application({
name: 'Fiddle',
launch: function () {
var form = new Fiddle.view.Form({
renderTo: document.body,
width: 600,
height: 400
});
}
});

How to make sanity io array select as multiselect?

I have a lot of tags, and I need to select a lot for each document. It is uncomfortable to click one by one every time. Also I see selected elements. How can I remake it into somefield like a multiselect? It could be even native. Or how to select all tags at once?
I am using array:
{
title: 'Language',
name: 'language',
type: 'array',
options: {
layout: 'grid'
},
of: [{
type: 'reference',
title: 'Lang',
to: {
type: 'settingLanguages'
}
}],
},
Dropdown example (Add field to schema):
{
title: 'Genre',
name: 'genre',
type: 'string',
options: {
list: [
{ title: 'Sci-Fi', value: 'sci-fi' },
{ title: 'Western', value: 'western' },
],
},
},
This is currently not possible out of the box with the default array component, but you should be able to create an input like this by building a custom input for it with the behaviour you want.
More on how to build custom inputs: https://www.sanity.io/docs/extending/custom-input-widgets

How to add empty rows to grid and fill the columns and then submit in EXTJS

How to add rows to grid and add some values with in the columns
and then submit the data as json format to the rest api in EXTJS
Json data like :
[
{
"col1" : "john",
"col2" : "xyz",
"col3" : "01/05/2018"
},
{
"col1" : "bush",
"col2" : "xpao",
"col3" : "01/08/2018"
},
.......
]
Some thing like above
I want to add data as above in grid column and submit to API in EXTJS
Thanks in Advance -:)
I want to add data as above in grid column and submit to API
For this, You need to use the store.sync() and rowediting plugin for grid.
store.sync() synchronizes the store with its proxy. This asks the proxy to batch together any new, updated and deleted records in the store, updating the store's internal representation of the records as each operation completes.
The Ext.grid.plugin.RowEditing plugin injects editing at a row level for a Grid. When editing begins, a small floating dialog will be shown for the appropriate row. Each editable column will show a field for editing. There is a button to save or cancel all changes for the edit.
You can check with working FIDDLE
Note : I have used dummy api name you need to use your actual api with store proxy for create, read, update and delete records.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.create('Ext.data.Store', {
storeId: 'gridStore',
fields: ['col1', 'col2', 'col3'],
autoLoad: true,
pageSize: 25,
remoteSort: true,
proxy: {
type: 'ajax',
method: 'POST',
api: {
read: 'data.json',
update: 'your_update_api',
create: 'your_create_api',
destroy: 'your_delete_api'
},
reader: {
type: 'json'
},
writer: {
type: 'json',
encode: true,
root:'data'
}
},
});
Ext.create('Ext.grid.Panel', {
title: 'Add Row Example',
store: 'gridStore',
height: 300,
border: true,
renderTo: Ext.getBody(),
tools: [{
xtype: 'button',
iconCls: 'fa fa-plus-circle',
tooltip: 'Add New Record',
handler: function () {
let store = this.up('grid').getStore();
store.insert(0, {
col1: '',
col2: '',
col3: ''
})
}
}],
columns: [{
text: 'col1',
dataIndex: 'col1',
flex: 1,
editor: {
xtype: 'textfield'
}
}, {
header: 'col2',
dataIndex: 'col2',
editor: {
xtype: 'textfield'
},
flex: 1
}, {
header: 'col3',
dataIndex: 'col3',
editor: {
xtype: 'datefield',
dateFormat: 'd/m/Y'
},
flex: 1
}],
plugins: [
Ext.create('Ext.grid.plugin.RowEditing', {
clicksToEdit: 1
})
],
bbar:['->',{
text:'Submit Changes',
handler:function(){
this.up('grid').getStore().sync()
}
}]
});
}
});
In real life situation you will need the following components:-
1- Ext.data.Model -> [Record] to map your model and add you validations and logic if needed.
2- Ext.data.Store -> [Set of records] which will consume the model.
3- Proxy which will handle the call the the web service, proxy can be added to the model or the store.
4- Grid Row Editor or cell row editor to be able to insert you data.
This is a working example FIDDLE
When you press Add data the model is created [record], you can start editing the data based on the editor property in the columns, this record is stored in the grid store so you can add as many records as you need.
When you press submit the store.sync is responsible to read the proxy apis and send the data to your service, you will need to open your dev tools network inspect to see the data sent in the request as json.

ExtJS: How to add new object to Store and send POST request to RESTful server

I am creating a Todo application with ExtJS 6. My application should fetch data, here are Todos list from a Server using RESTful web service, and add new Todo task and send this new task to store in the Server. For the fetching part, it was successfully to show the data from the server, but I still facing a problem with the adding data and send back to the server.
Here are components that I have:
A View called ToDoList which contains 1 textfield and 1 button to add new Todo task:
Ext.define('ToDo.view.toDoList.ToDoList',{
extend: 'Ext.panel.Panel',
requires: [
'ToDo.view.toDoList.ToDoListController',
'ToDo.view.toDoList.ToDoListModel'
],
xtype: 'app-todoList',
controller: 'todoList',
viewModel: {
type: 'todoList'
},
items: [{
xtype: 'container',
items: [{
xtype: 'container',
layout: 'hbox',
cls: 'task-entry-panel',
defaults: {
flex: 1
},
items: [{
reference: 'newToDo',
xtype: 'textfield',
emptyText: 'Enter a new todo here'
},
{
xtype: 'button',
name: 'addNewToDo',
cls: 'btn-orange',
text: 'Add',
maxWidth: 50,
handler: 'onAddToDo'
}]
}]
}]
});
My ViewModel called ToDoListModel which contains store object named todos, which including 3 parameters: id, desc and done.
Ext.define('ToDo.view.toDoList.ToDoListModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.todoList',
stores:{
todos: {
fields: [{
name: 'id',
type: 'string'
},
{
name: 'desc',
type: 'string'
},
{
name: 'done',
type: 'boolean'
}],
autoLoad: true,
sorters: [{
property: 'done',
direction: 'ASC'
}],
proxy: {
type: 'rest',
url: 'http://localhost:8082/RegistrationForm/tasks/',
reader: {
type: 'json'
},
writer: {
type: 'json'
}
}
}
}
});
And My ViewController called: ToDoListController looks like:
Ext.define('ToDo.view.toDoList.ToDoListController', {
extend: 'Ext.app.ViewController',
alias: 'controller.todoList',
views: ['ToDo.view.toDoList.ToDoList'],
init: function(){
var me = this;
this.getViewModel().data.todos.load(function(records){
Ext.each(records, function(record){
console.log(record);
me.addToDoToView(record);
});
});
Ext.getBody().on('click', function(event, target){
me.onDelete(event, target);
}, null, {
delegate: '.fa-times'
});
},
onAddToDo: function () {
var store = this.getViewModel().data.todos;
var desc = this.lookupReference('newToDo').value.trim();
**HOW CAN I ADD NEW OBJECT TO STORE AND PERFORM POST REQUEST HERE TO THE SERVER**
},
addToDoToView: function(record){
this.view.add([{
xtype: 'container',
layout: 'hbox',
cls: 'row',
items: [{
xtype: 'checkbox',
boxLabel: record.get('desc'),
checked: record.get('done'),
flex: 1
},
{
html: '<a class="hidden" href="#"><i taskId="'+ record.get('id') + '" class="fa fa-times"></i></a>'
}]
}]);
},
onDelete: function(event, target){
var store = this.getViewModel.data.todos;
var targetCmp = Ext.get(target);
var id = targetCmp.getAttribute('taskId');
store.remove(store.getById('id'));
store.sync({
success: function(){
this.view.remove(targetCmp.up('.row').id)
},
scope: this
});
}
});
And when the user click to the Add Button, the function onAddToDo() in the Controller will be call, here it will get the new task from the textfield (here desc variable), create a new Todo object and then send this object to the server using POST request. My service in the server will look like:
//ADD new Todo
#RequestMapping(value = "/tasks/", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(#RequestBody Todo todo, UriComponentsBuilder ucBuilder) {
//add this new Todo object to the database
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
What I would like to do is after user entering new Task, I will pack it as new object, structure look like: {'id'=1, 'desc'='what user entered','done'=false} and send it as the RequestBody of the POST method to the server. Can anyone guide me how can I do that? Thank you
You can add the record to the store by using add() function.
store.add({id:1, desc:'what user entered',done:false});
Then you can call sync() function which will call the create api of the store and send the inserted data to server.
store.sync();
sync() synchronizes the store with server in 3 cases that are new record creation, record updation and record deletion. So here when we add a new record and call sync(), it will send only the newly added record to the server as it is not present at server to the URL specified in store

use form for add and update data,how is that possible in extjs4?

i have a form panel and a tree panel
the form is used to add new users, the tree is used to show the list of users
what i want is to right click a node in my tree ,click edit (already can do that) then i have my data in the add form panel and be able to modify there and update my user
so basically use the same form for adding and updating
this is how im trying to do up to know
but its not working at all
i added a model to my tree,i used loadRecord(rec), but i dont know how to bind my tree data with the form fields!
tried adding displayfield with same name from my tree model!!
my tree model and store:
Ext.define('TreeModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'text' },
{ name: 'id' }
]
});
window.ATreeStore = Ext.create('Ext.data.TreeStore', {
model: 'TreeModel',
root: Ext.decode(objt.TreeToJson()),
proxy: {
type: 'ajax'
},
sorters: [{
property: 'leaf',
direction: 'ASC'
}, {
property: 'text',
direction: 'ASC'
}]
});
my tree menu:
var myContextMenu = new Ext.menu.Menu({
items: [{
text: 'Edit',
handler: function () {
Ext.getCmp('addaform').getForm().loadRecord(rec);
}
}
}]
my form:
Ext.define("Ext.app.Adduser", {
extend: "Ext.form.Panel",
title: 'Add user',
id : 'addform',
closable: true,
collapsible: true,
animCollapse: true,
draggable: true,
resizable: true,
margin: '5 5 5 5',
height: 400,
frame: true,
fieldDefaults: {
labelAlign: 'top',
msgTarget: 'side'
},
defaults: {
anchor: '100%'
},
items: [{
layout: 'column',
border: false,
items: [{
padding: '5',
columnWidth: .5,
border: false,
layout: 'anchor',
defaultType: 'textfield',
items: [{
fieldLabel: ' Name',
name: 'name',
allowBlank: false,
displayfield:'id',//
anchor: '95%'
}]
}, {
padding: '5',
columnWidth: .5,
border: false,
layout: 'anchor',
defaultType: 'textfield',
items: [{
fieldLabel: 'First name',
name: 'fname',
allowBlank: false,
anchor: '95%'
}, {
xtype: 'textarea',
fieldLabel: 'Profile',
name: 'prof',
anchor: '95%'
}]
}],
buttons: [{
text: 'Save',
handler: function () {
this.up('form').getForm().submit
({
url: 'AddData.ashx',
params: { action: 'add' },
success: function (form, action)
{
Ext.MessageBox.show({ title: 'Success !',
msg: 'User added successfully<br />',
icon: Ext.MessageBox.INFO,
buttons: Ext.MessageBox.OK
}) }
}) }]
thank you
If your TreePanel uses a TreeStore which in turn has a Ext.data.Model, then when you right click on a node (say nodeA), you should be just able to do form.loadRecord(nodeA), the API for loadRecord is here
If it's still not clear, I think this blog post could help, it talks about loading a grid record into a form. I know it's not ExtJS 4, but the key functions are the same.
Ok, let me show this with a super simple example, hope it will help.
First we create the form that we want to display stuff in, note that the renderTo property is bound to a div inside my HTML, so you might need to change that. Also note that the name property of the textarea and textfield, they are the key for the loadRecord to work, they have to match the fields defined in the model later.
var form = Ext.create('Ext.form.Panel',{
renderTo : 'form-div',
items : [{
xtype : 'textarea',
fieldLabel : 'label 1',
name : 'name'
},{
xtype : 'textfield',
fieldLabel : 'label 2',
name : 'age'
}]
});
Next, we create the tree to display our data, we start by creating a simple model :
Ext.define('Person',{
extend : 'Ext.data.Model',
fields : [{
name : 'name',
type : 'string'
},{
name : 'age',
type : 'int'
}]
});
Then we create a TreeStore that uses that model and initialize it with some inline data:
var store = Ext.create('Ext.data.TreeStore',{
model : 'Person',
root : {
expanded : true,
children : [{
name : 'John',
age : 10,
leaf : true
},{
name : 'Joe',
age : 100,
leaf : true
}]
}
});
Then we create the tree to display the data in the store. (Note that the nodes will show up as "undefined" because we are not using the default "text" property of a Node)
var tree = Ext.create('Ext.tree.Panel',{
height : 300,
width : 300,
store : store,
rootVisible : false,
renderTo : 'tree-div',
listeners : {
itemclick : function(view, record){
form.loadRecord(record);
}
}
});
Now, you should see a tree with two nodes both displayed as "undefined" on your page, as well as a Form with a textarea and a textfield. If you click on a node inside the tree, the form will display the name and age of the selected node.