Sencha Touch. NestedList move, insert, remove items - extjs

My question is about how to move, insert and remove items in NestedList
I have a NestedList like this:
// MODEL
Ext.define('MyApp.model.Comments', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'text', type: 'string'}
]
}
});
// NESTEDLIST
Ext.define('MyApp.view.Comments', {
requires: [
'Ext.data.TreeStore'
],
config: {
id: 'comments',
store: {
type: 'tree',
root: {},
model: 'MyApp.model.Comments',
proxy: {
type: 'ajax',
actionMethods: {read: 'POST'},
url: './comments.php',
timeout: 4000,
enablePagingParams: false,
extraParams: {
action: 'get-comments'
}
}
},
displayField: 'text',
detailCard: {
xtype: 'container',
layout: 'vbox',
items: [
{
id: 'comment-text-field',
flex: 1
}
]
},
listeners: {
leafitemtap: function(el, list, index, target, record) {
var detailCard = el.getDetailCard(),
text = record.data.text,
commentField = detailCard.query("#comment-text-field");
var textHtml = '<b>text: </b>' + text;
if (commentField) {
commentField[0].setHtml(textHtml);
}
},
destroy: function(el) {
el.getDetailCard().destroy();
}
}
}
});
// './comments.php' response
[
{
"text": "New comments",
"children": [
{
"text": "NestedList provides a miller column interface to navigate between",
"leaf": true
},
{
"text": "The absolute bottom position of this Component; must be a valid CSS length value",
"leaf": true
}
]
},
{
"text": "Checked comments",
"children": [
{
"text": "Whether or not this Component is absolutely centered inside its Container",
"leaf": true
},
{
"text": "Component is absolutely centered inside its Container",
"leaf": true
},
{
"text": "More comments",
"leaf": true
}
]
}
]
It works fine but i need to move, remove and add items in NestedList.
F.e. when i open one of "New comments" (leaf item is opened and leafitemtap fired), opened item should be removed from "New comments" and inserted into "Checked comments".
I see 2 ways to do it:
Send request to update comments.php response AND update store via Sencha functionality
Send request to update comments.php response AND reload comments.php each time when some items are clicked to update NestedList
Of course 1st way is better, but I have no idea how to do at least one of them.
Hope you will help me, thsnk's!

Well, looks like there is no convenient way to do that. Here is comment on official forum:
http://www.sencha.com/forum/showthread.php?205097-How-to-create-a-Nested-List-with-dynamic-loading-of-each-node

Related

Loading nested GeoJson to FeatureStore with hasMany. Possible?

Is it possible to use associations while loading nested GeoJson to FeatureStore via vectorLayer?
Ext.define('Employee', {
extend: 'Ext.data.Model',
proxy: {
type: 'memory',
reader: {
type: 'json',
idProperty: 'id'
}
},
fields: [ { name: 'name', type: 'string' } ]
});
Ext.define('Company', {
extend: 'Ext.data.Model',
proxy: {
type: 'memory',
reader: {
type: 'json',
idProperty: 'id'
}
},
fields: [ { name: 'name', type: 'string' } ],
hasMany: { model: 'Employee', name: 'employees' }
});
var jsonData = {
companies: [
{
name: 'Foo',
employees: [
{ name: 'Jack' },
{ name: 'Joe' }
]
},
{
name: 'Bar',
employees: [
{ name: 'Jim' }
]
}
]
};
Ext.define('CompaniesExt', {
extend: 'Ext.data.Store',
model: 'Company',
data: jsonData,
storeId: 'CompaniesExt',
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'companies'
}
}
});
Ext.define('CompaniesGeoExt', {
extend: 'GeoExt.data.FeatureStore',
model: 'Company',
storeId: 'CompaniesGeoExt'
});
// data from json
var jsonStore = Ext.create('CompaniesExt');
// data from geoJson
var map = new OpenLayers.Map({ allOverlays: true });
var geoJsonStore = Ext.create('CompaniesGeoExt');
var layer = new OpenLayers.Layer.Vector('Companies', {
storeName: 'CompaniesGeoExt',
strategies: [
new OpenLayers.Strategy.Fixed(),
],
protocol: new OpenLayers.Protocol.HTTP({
url: "/companies.geojson",
format: new OpenLayers.Format.GeoJSON(),
})
});
map.addLayers([layer]);
geoJsonStore.bind(layer);
So, the first jsonStore works as expected, the employeesStore gets populated for each company. The second geoJsonStore does not. Employees data remain in raw.data and the sub-stores don't ge populated on load.
Is it supposed to work this way, or am I missing something?
Here's the contents of companies.geojson:
{
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "point",
"coordinates": [ 0, 0 ]
},
"type": "feature",
"properties": {
"name": "Foo",
"employees": [
{ "name": "Jack" },
{ "name": "Joe" }
]
}
},
{
"geometry": {
"type": "point",
"coordinates": [ 1, 1 ]
},
"type": "feature",
"properties": {
"name": "Bar",
"employees": [
{ "name": "Jim" }
]
}
}
]
}
It seems, that the easiest way is to rewrite data after loading features, for example on "featuresadded" event:
rewriteEmployees: function(event){
// which store
var store = event.features[0].layer.store;
// for each item do the rewrite
store.each(
function(r){
if (r.raw.data.emplyees)
r.raw.data.employees.forEach(function(e){
r.employees().add(e);
});
r.employees().commitChanges();
}
);
},

Connect two models by ID

I asked an ExtJS question a few days ago, and as a side note I also asked how I could connect my two models. Main answer got answered, but I still couldn't figure out my other problem, so I am opening a new question for it.
It might be a silly problem again, but here it is:
I get a JSON from the server, that looks like this:
{
"success": true,
"result": {
"publishers": [
{
"id": "009999",
"type": "ABC",
"isReceipient": false,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": true,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": false,
"description": ""
}
],
"notes": [
{
"publisherId": "009999",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": "46",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": 45,
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
}
]
}
}
So I get two arrays, publishers and notes. I have two model for that, I load them in the models by the controller using loadRawData(), it works, I got all my publishers and notes in the store. (They both have a store - Publishers and Notes). But then I need to use the publisherId in notes to display publishers description. I tried a lot of things I could find using google and sancha docs: associations, hasmany, hasone, belongsto and creating a third store consisting of the two aggregated model. Nothing worked so far.
What I want is to have a store that has every notes, plus all notes have the publisher info.
I'll copy my two models below, you can see there, commented out what I have been trying. I also tried changing ID's, names etc., so variations of these. But I could never get the Notes to have the publisher's info.
Ext.define('MA.model.Note', {
extend: 'Ext.data.Model',
fields: [
'publisherId',
'text' ,
//hasone publisher
{
name: 'created',
type: 'date',
dateFormat: 'c'//'d-M-Y H:i:s' //"2014-02-23T18:24:06.074Z" needed: "02/23 18:24"
}
]
// hasOne: [
// {
// name: 'publisher',
// model: 'Publisher',
// associationKey: 'publisherId'
// }
// ],
// associations: [
// {
// type: 'hasOne',
// model: 'Publisher',
// primaryKey: 'id',
// foreignKey: 'publisherId'
// }
// ]
// associations : [
// {
// type : 'hasOne',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'User',
// associationKey : 'User'
// },
// {
// type : 'belongsTo',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'Publisher',
// associationKey : 'publisherId'
// }
// ]
// belongsTo: [
// {
// model: 'MA.model.Publisher',
// name: 'Note',
// primaryKey: 'publisherId',
// foreignKey: 'id',
// // foreignStore: 'Publishers'
// }
// ]
});
Publisher:
Ext.define('MA.model.Publisher', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
'id',
'type' ,
{
name:'isReceipient',
type:'boolean'
},
'description'
]
// hasMany: [
// {
// model: 'MA.model.Note',
// name: 'Note',
// primaryKey: 'id',
// foreignKey: 'publisherId',
// // foreignStore: 'Notes'
// }
// ],
});
Am I even on the right track? Should I use associations? I couldn't even really get the difference between associations and hasMan/One/belongTo properties, I guess there isn't any really, just the way you declare it.
Edit: My idea is to have a DataView class, that has a store which holds the notes and the corresponding publisher to the notes. I have a main panel:
items: [
{
xtype: 'create-note-panel',
flex: 1
},
{
xtype: 'notes-panel',
store: 'Notes',
flex: 1
}
]
And my notes-panel looks something like this:
Ext.define('MA.view.sections.notes.NotesPanel' ,{
extend: 'Ext.DataView',
alias: 'widget.notes-panel',
// deferInitialRefresh: true,
itemSelector: 'div.notes-list',
tpl: new Ext.XTemplate(
'<div class="notes-list">',
'<tpl for=".">',
'<p>{created}, by {publisherId}</p>',
'<p>{text}</p>',
'<hr />',
'</tpl>',
'</div>'
),
emptyText: 'No data available',
initComponent: function() {
var me = this,
publisherStore = Ext.data.StoreManager.lookup('Publishers');
//me.addEvents( //just messing here, trying stuff
// 'user-offer-activities'
//);
me.callParent(arguments);
}
//renderTo: Ext.getBody()
})
;
Notice the publisherId in the template. I need the publisher description there. I didn't want to use grid, as this DataView seemed a pretty good solution, I thought joining two stores would be easy, I just couldn't figure it out yet :(
I have created a fiddle that results in what you are after (displaying data from both models in the View).
It is a bit of a longwinded approach though, because of the way the tpl works you don't have access to the Model, just the data within it. So I created a function on the tpl that gets the record we're interested in from the model based on the publisherId.
https://fiddle.sencha.com/#fiddle/o9o
Note: I created the fiddle without using any associations between the models, but you could probably create a hasOne association from the Notes to Publisher with a foreignKey linking the id and publisherId in the respective models (though I still don't think this would enable you to refer to the members directly in the tpl).

kendo ui: how to remove a dataItem of a dataSource bound to some comboBox, inside combobox dataBound event

I have a grid, inside of some column of which I have created a combobox editing UI, using columns.editor function.
My goal is every time a user selects some value from the combobox -while populating a newly created grid record-, this value to be
removed from the list options of a next record's combobox.
One of the things i've tried is shown below:
function equipmentDropDownEditor(container, options) {
var equipmentComboBox = $('<input id="equipmentDropDownEditor" required data-text-field="name" data-value-field="name" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
autoBind: false,
dataSource: equipmentTypesDS,
dataBound: function(e) {
var equipmentData = e.sender.dataSource.data();
if(currentlyInsertedEquipmentTypes.length > 0){
for(var i=0;i<currentlyInsertedEquipmentTypes.length;i++){
$.each( equipmentData, function( index, selectedEquipmentData ) {
if (selectedEquipmentData.name == currentlyInsertedEquipmentTypes[i]){
var dataItem = e.sender.dataSource.at(index);
console.log("dataItem: " + dataItem.name + " is being removed");
e.sender.dataSource.remove(dataItem);
}
});
}
}
}
});
}
I've created a global array variable named "currentlyInsertedEquipmentTypes" inside of which I hold all the user's already selected values
(for example if the user has created 2 records inside the grid and has selected "laptop" option in the combobox of the first and "workstation" option
in the combobox of the second --> currentlyInsertedEquipmentTypes = ["laptop", "workstation"] ).
Inside the combobox dataBound event I check whether the user has already selected values (currentlyInsertedEquipmentTypes.length>0)
and if he has, I locate the corresponding object inside the bound dataSource and I remove it, so that it wont be available in the next record's combobox list.
This is where the whole thing crashes even though the data item removal takes place.
Am i missing something that i should do after the data item removal? Should i rebind the datasource to the combobox in some way?
Any help would be much appreciated.
[EDIT]
---- The combobox datasource code
var equipmentTypesDS= new kendo.data.DataSource({
transport: {
read: {
url: "api/equipment_types",
type: "GET",
data: {
//"equipment_category": 1
},
dataType: "json"
}
},
schema: {
data: "data",
total: "total"
}
});
--- the kendo grid code:
$("#popup_equipment").kendoGrid({
dataSource: {
schema:{
model:{
id: "equipment_type_id",
fields:{
equipment_type_id: { editable: false },
name: { }, //validation: {required: true}, defaultValue: "LAPTOP",
items:{ type: "number", defaultValue:1, validation: { required: true, min: 1} }
}
}
}
},
toolbar: ["create"],
columns: [
{ field: "name", title: "εξοπλισμός", width: "300px", editor: equipmentDropDownEditor, template: "#=name#" },
{ field: "items", title:"πλήθος", width: "80px"},
{ command: ["destroy"], title: " ", width: "100px" }
],
//editable: "inline",//true,
editable:{confirmation: false},
scrollable: false,
selectable: false
});
[EDIT 2]
$("#popup_equipment").kendoGrid({
dataSource: {
schema:{
model:{
id: "equipment_type_id",
fields:{
equipment_type_id: { editable: false },
name: { }, //validation: {required: true}, defaultValue: "LAPTOP",
items:{ type: "number", defaultValue:1, validation: { required: true, min: 1} }
}
}
}
},
toolbar: ["create"],
columns: [
{ field: "name", title: "εξοπλισμός", width: "60%", editor: equipmentDropDownEditor, template: "#=name#" },
{ field: "items", title:"πλήθος", width: "20%"},
{ command: ["destroy"], title: " ", width: "20%" }
],
editable:{confirmation: false},
scrollable: false,
selectable: false,
save: function(e){
console.log("GRID SAVE EVENT! ", e);
var equipment_name = e.values.name;
equipmentTypesDS.get(equipment_name).used = true;
console.log("equipmentTypesDS", equipmentTypesDS);
console.log("END OF GRID SAVE EVENT!");
}
});
function equipmentDropDownEditor(container, options) {
var equipmentComboBox = $('<input id="equipmentDropDownEditor" required data-text-field="name" data-value-field="name" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
autoBind: false,
dataSource: equipmentTypesDS,
});
}
var equipmentTypesDS= new kendo.data.DataSource({
transport: {
read: {
url: "api/equipment_types",
type: "GET",
data: {
//"equipment_category": 1
},
dataType: "json"
}
},
schema: {
data: "data",
total: "total",
model:{
id: "name"
}
},
filter: { field: "used", operator: "neq", value: true }
});
I would suggest a different approach. Instead of removing the element filter it out.
Example: I define a DataSource with a list of Cities (your Inserted Equipment) as follow:
var cityDS = new kendo.data.DataSource ({
data : [
{ City : "Seattle", used : false },
{ City : "Tacoma", used : false },
{ City : "Kirkland", used : false },
{ City : "Redmond", used : false },
{ City : "London", used : false },
{ City : "Philadelphia", used : false },
{ City : "New York", used : false },
{ City : "Boston", used : false }
],
schema : {
model : {
id : "City"
}
},
filter: { field: "used", operator: "eq", value: false }
});
As you can see I added a field called used that simply says if that City is already used or not. And I set it as id of this DataSource. In addition, I set a filter saying that I only want those where used id equal (eq) to false.
The editor function is pretty much yours:
function cityDropDownEditor(container, options) {
var equipmentComboBox = $('<input required data-text-field="City" data-value-field="City" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
autoBind: false,
dataSource: cityDS
});
}
but with no dataBound or any other event handler.
Finally in the Grid when I save a record, I filter that city from the list. Something like:
var grid = $("#grid").kendoGrid({
dataSource: ds,
editable : "popup",
pageable : true,
toolbar: [ "create" ],
columns :
[
{ field: "FirstName", width: 90, title: "First Name" },
{ field: "LastName", width: 200, title: "Last Name" },
{ field: "City", width: 200, editor : cityDropDownEditor }
],
save : function(e) {
console.log("e", e);
var city = e.model.City;
cityDS.get(city).used = true;
}
}).data("kendoGrid");
This might work if you start the Grid with no elements otherwise you would have to conveniently initialize the used field. It might require some additional code dealing with cases as changing the City but from your description, doesn't seem to be the case.
You can see this running here : http://jsfiddle.net/OnaBai/ZH4aD/

Backbone-UI, TableView, rendering columns

I'm trying to render a table view with four columns, 'name', 'birthday', 'gender', 'married', but
a) they columns aren't showing up at all
b) I'm not even sure if I am passing them correctly, because when I console.log table.options the columns property is rendered as "empty":
Object {columns: Array[0], emptyContent: "no entries", onItemClick: function, sortable: false, onSort: null}
I've tried this:
var table = new Backbone.UI.TableView({
model: people,
columns: [
{ title: "Name", content: 'name' },
{ title: "Gender", content: "gender" } },
{ title: "Birthday", content: "birthday" } },
{ title: "Married", content: "married" } }
]
});
And this:
var table = new Backbone.UI.TableView({
model: people,
options: {
columns: [
{ title: "Name", content: 'name' },
{ title: "Gender", content: "gender" },
{ title: "Birthday", content: "birthday" },
{ title: "Married", content: "married" }
]
}
});
The source code change is adding the options as mu is too short said. Change the initialize method of the Backbone.UI.TableView object to be the following within the source code:
initialize : function(options) { //add parameter
Backbone.UI.CollectionView.prototype.initialize.call(this, arguments);
$(this.el).addClass('table_view');
this._sortState = {reverse : true};
this.options = _.extend({}, this.options, options); //Add this line
}
I'm sure there might be a better place to put this but I'm just going through tutorials to learn some advanced backbone stuff considering the documentation does not match the current version I would shy away from using the library in production as of right now. Hopefully it is fixed in the future.

filter option in a grid

i use a filter option in a grid where i can select per column what i want to filter. For the options i need to do this:
Ext.ux.ajax.SimManager.init({
delay: 300,
defaultSimlet: null
}).register({
'filterEvents': {
data: [
['Dag 1', 'Dag 1'],
['Dag 2', 'Dag 2']
],
stype: 'json'
}
});
dagenFilter = Ext.create('Ext.data.Store', {
fields: ['id', 'text'],
proxy: {
type: 'ajax',
url: 'filterEvents',
reader: 'array'
}
});
But because the filter options need to be dynamic and not static i want to load the options from a service. The output of that json looks like this
{
"dagen": [{
"name": "Dag 1",
"reference": "Dag 1"
}, {
"name": "Dag 2",
"reference": "Dag 2"
}, {
"name": "Dag 3",
"reference": "Dag 3"
}, {
"name": "Dag 4",
"reference": "Dag 4"
}]
}
but i am not sure how to do this.
If by "filter option in a grid" you are referring to FiltersFeature the answer is easy. A ListFilter can be backed by a DataStore.
Take a look at the options config of ListFilter here. Specifically, store and phpMode might also be relavant. Here is an example of one:
{
header: 'List Filter Column',
dataIndex: 'list_data',
width: 120,
filter: {
type: 'list',
store: Ext.getStore('FilterOptions'),
phpMode: true
}
}

Resources