I've searched and I can see this has been asked quite a few times, but I cant even figure out how to do a simple console.log on the data.
My store:
Ext.define('AventosPlanningTool.store.Aventos', {
extend:'Ext.data.Store',
config:
{
model:'AventosPlanningTool.model.Aventos',
proxy:
{
type:'ajax',
url:'resources/json/frames.json',
reader:
{
type:'json',
rootProperty:'options'
}
},
autoLoad: true
}
});
I can see in my network tab that the JSON file IS loading. I cannot figure out what to do with it at this point. In the data store, I've set the model to AventosPlanningTool.model.Aventos which is the file below.
Ext.define('AventosPlanningTool.model.Aventos', {
extend:'Ext.data.Model',
xtype:'AventosModel',
config:
{
fields: [
'name',
'image'
]
}
});
My JSON is pretty simple right now:
{
"name": "Cabinet Type",
"options": [
{
"name": "Face Frame",
"image": "resources/images/aventos/frames/faceframe.png"
},
{
"name": "Panel",
"image": "resources/images/aventos/frames/panel.png"
}
]
}
Even if I can do a console.log on the data that would be very helpful. I can't figure out how to use the data. I've checked both guides in the docs: http://docs-origin.sencha.com/touch/2.2.1/#!/guide/models, http://docs-origin.sencha.com/touch/2.2.1/#!/guide/stores and I just can't grasp it
Add a load listener to your store:
Ext.define('AventosPlanningTool.store.Aventos', {
extend:'Ext.data.Store',
config: {
model:'AventosPlanningTool.model.Aventos',
proxy: {
type:'ajax',
url:'resources/json/frames.json',
reader: {
type:'json',
rootProperty:'options'
}
},
autoLoad: true,
listeners: {
load: function(st, g, s, o, opts) {
st.each(function(record) {
console.log(record.get('name') + ' - ' + record.get('image'));
});
}
}
});
In sencha, data is defined in models, and actually memorized in stores. You can load your JSON through a proxy. Think of the model as the tables from sql and the store as the actual data in tables. Now, if you want to get the data from your store and perform operations on it, you have to load the store. To fetch your data into a list, you define a list with xtype:'list' specify your store store:'yourStoreName' and provide a template for showing that data. Here's a very detailed explanation on what I tried to say.
http://docs-origin.sencha.com/touch/2.2.1/#!/api/Ext.data.Store
also this:
http://miamicoder.com/2012/how-to-create-a-sencha-touch-2-app-part-2/
Look at the API docs for the data store. Note that you can only access the data once the store has been loaded. For example:
store.load();
store.getAt(0) // null, the store load hasn't completed yet.
You can loop over each record in the store using the each method. You can get a record at a particular index using getAt
store.each(function(rec) {
console.log(rec.get('name'), rec.get('image'));
});
console.log(store.getAt(0).get('name'));
Often times you will bind the store to a list, there are plenty of examples of this in the API docs.
I think what you're missing is to listen for the 'load' event on the store.
store.on('load', function(thisStore, records) {
console.log(records[0].get('name'));
})
Related
Using ExtJS 6 one can have a store bind to the model and use the methods sync to save or load to load data.
I imagine that if a data is removed from store, upon calling sync the data will be removed from database too.
In my use case, I have different URLs and mandatory Ajax query fields for each action of create/update, load and delete data.
I have only seen examples showing load or save to storage, how can I declare the load, save and delete using Ajax in the same model?
Another doubt I have is that stores themselves can have a proxy, so they can perform those operations too, at least the load operation that I have seen in use. What's the difference between having these on the model or store? What's the best practice?
Example model from Sencha docs (is this only for read?):
Ext.define('MyApp.model.Base', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int'
}],
schema: {
namespace: 'MyApp.model', // generate auto entityName
proxy: { // Ext.util.ObjectTemplate
type: 'ajax',
url: '{entityName}.json',
reader: {
type: 'json',
rootProperty: '{entityName:lowercase}'
}
}
}
});
Another example I found on https://examples.sencha.com/extjs/6.0.1/examples/classic/writer/writer.html using the proxy config, this seems more like what I would need as it specifies a URL for each operation:
var store = Ext.create('Ext.data.Store', {
model: 'Writer.Person',
autoLoad: true,
autoSync: true,
proxy: {
type: 'ajax',
api: {
read: 'app.php/users/view',
create: 'app.php/users/create',
update: 'app.php/users/update',
destroy: 'app.php/users/destroy'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
},
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show({
title: 'REMOTE EXCEPTION',
msg: operation.getError(),
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
},
listeners: {
write: function(proxy, operation){
if (operation.action == 'destroy') {
main.child('#form').setActiveRecord(null);
}
Ext.example.msg(operation.action, operation.getResultSet().message);
}
}
});
I believe I can have something like this in my case (this is just an example not tested!):
Ext.define('My.Person.Model', {
proxy: {
type: 'ajax',
api: {
read: 'http://myapiserver/getuser',
create: 'http://myapiserver/upsertuser',
update: 'http://myapiserver/upsertuser',
destroy: 'http://myapiserver/removeuser'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
},
// How can I have the parameters for each one?
extraParams : {
isuserUnderage : ' '
, query : '%'
}
}
});
I have no idea how to do this, specially specifying parameters for each type of Ajax request (read, create, update, destroy), I can have an upsert request that will send all fields, but the remove request will require only the ID, the get request can have optional fields for filtering, like filtering persons by name.
Example to be more clear of the problem.
Example data:
[
{
"id": "1",
"name": "Fred",
"age": 21,
"sex": "m"
},
{
"id": "2",
"name": "Susan",
"age": 12,
"sex": "f"
},
{
"id": "3",
"name": "Marcus",
"age": 22,
"sex": "m"
},
{
"id": "4",
"name": "Alex",
"age": 32,
"sex": "m"
}
]
Endpoints example:
Endpoints have parameters, these are mandatory, this means that calling an enpoint without a parameter will cause a server error, also passing a parameter that is not specified will cause a server error! If a parameter is not necessary one can pass a string with a single whitespace .
To read:
Endpoint: http://myapiserver/getuser?query={query}
Name is a filter by name, for example http://myapiserver/getuser?query=fred will bring users with name that has the string fred.
To write, we usually have an upsert, so it works for both insert and update:
Endpoint: http://myapiserver/upsertuser?id={id}&name={name}&age={age}&sex={sex}
So to update we can pass the ID: http://myapiserver/upsertuser?id=1&name=Frederick&age=21&sex=m and to insert we pass an empty string for ID: http://myapiserver/upsertuser?id= &name=Maurice&age=41&sex=m
To remove:
Endpoint: http://myapiserver/removeuser?id={id}
Example: http://myapiserver/removeuser?id=1, removes person with ID 1.
Because you say it's mandatory to use GETs with query params, I would encourage you to rethink your tech stack because the RESTful verbs really make it more clear what your action is, and you remove the actual action from your URL routes. However, I know sometimes this is totally out of our control, so I'll try my best here... I have to say, I've never experienced something like this, so I don't know if what I'm showing here is a best practice.
I can't show a true implementation because Sencha Fiddle is a simple sandbox, not meant for actual server-side implementations. I'm also assuming that you're using the classic toolkit, but if you need it in modern, it's a fairly easy port that you can do.
I prefer the proxy inside of the model for several reasons... if I need to use this model in several different stores throughout my app, then each store will inherit the same proxy. If I want to use the same model, but I don't want its proxy, I can simply override it when defining the store. Also, if the proxy doesn't exist on the model, then the framework assumes what your URL should be, which doesn't work when I want to use models individually.
I think I've come up with what you're asking for in this Fiddle. Really the core of what you want is in GETUser.js.
// We need to create our own proxy that will handle this for us
Ext.define('AjaxGet', {
extend: 'Ext.data.proxy.Ajax',
alias: 'proxy.ajaxGet',
// Per your requirement, we want to send individual requests
batchActions: false,
createOperation: function (action, config) {
// This means we're doing an action against one of our records
if (config && config.records) {
if (action === 'destroy') {
config.params = config.records[0].getDeleteParams();
} else if (action === 'create' || action === 'update') {
config.params = config.records[0].getUpsertParams();
}
}
return this.callParent(arguments);
}
});
// This is the desired, "GET" User model that uses GETs and query params for all actions
Ext.define('GETUser', {
extend: 'Ext.data.Model',
idProperty: 'Id',
fields: [{
name: 'Name',
type: 'string'
}, {
name: 'Id',
type: 'int'
}, {
name: 'Age',
type: 'int'
}, {
name: 'Sex',
type: 'string'
}],
proxy: {
type: 'ajaxGet',
api: {
read: 'Users',
create: 'upsertuser',
update: 'upsertuser',
destroy: 'removeuser'
},
actionMethods: {
create: 'GET',
update: 'GET',
destroy: 'GET'
}
},
getUpsertParams: function () {
const data = this.getData();
// Means this record hasn't been saved, so we're in the CREATE state
if (this.phantom) {
// We don't want to send the ID with what the framework sets as the ID
data.Id = undefined;
}
return data;
},
getDeleteParams: function () {
return {
Id: this.get('Id')
};
}
});
So what I ended up doing was creating a custom proxy that overrides the createOperation method to check which operation we're doing... based on that operation, we use the methods in the model to retrieve the params we want to send to the API. You need actionMethods in the proxy because otherwise, they default to POSTs.
I would like to send data with setActiveItem() when doing view change from this store:
Ext.define('SkSe.store.Places',{
extend:'Ext.data.Store',
config:{
autoDestroy: true,
model:'SkSe.model.Places',
//hardcoded data
data: [
{
name: 'Caffe Bar XS', //naziv objekta
icon: 'Icon.png', //tu bi trebala ići ikona kategorije kojoj pripada
stamps: 'stamps1' //broj "stampova" koje je korisnik prikupio
},
{
name: 'Caffe Bar Mali medo',
icon: 'Icon.png',
stamps: 'stamps2'
},
{
name: 'Caffe Bar VIP',
icon: 'Icon.png',
stamps: 'stamps3'
}
]
//dynamic data (Matija)
//remember to change the icon path in "Akcije.js -> itemTpl"
/*proxy:{
type:'ajax',
url:'https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AIzaSyCFWZSKDslql5GZR0OJlVcgoQJP1UKgZ5U',
reader:{
type:'json',
//name of array where the results are stored
rootProperty:'results'
}
}*/
}
});
This is my my details controller that should get data from places store:
Ext.define('SkSe.controller.Details', {
extend: 'Ext.app.Controller',
config: {
refs: {
placesContainer:'placesContainer',
Details: 'details'
},
control: {
//get me the list inside the places which is inside placesContainer
'placesContainer places list':{
itemsingletap:'onItemTap'
//itemtap:'onItemTap'
}
}
},
onItemTap:function(list,index,target,record){
console.log('omg');
var addcontact= Ext.create('SkSe.view.Details',
{
xtype:'details',
title:record.data.name,
data:record.data
});
var panelsArray = Ext.ComponentQuery.query('details');
console.log(panelsArray);
Ext.Viewport.add(addcontact);
addcontact.update(record.data.name);
Ext.Viewport.setActiveItem(addcontact);
console.log(record.data.name);
}
});
I would like to send name record from the places.js model above. I heard that you can't pass data with setActiveItem but should create a function that updates view(No I can't use pop and push here).
I'm not very familiar with sencha touch syntax and I'm not sure what functions to use to do that, clearly update function is not that.
I switched up your logic slightly but have provided a working example of what I think you are trying to do.
SenchaFiddle is a little different from what I get running on my machine but you should be able to get the idea.
The initial list loaded gets the data from your store, and on itemtap will push a new view onto the viewport.
You will need to add a navigation button to get back to the list from view.View as well as a better layout for the details. I would suggest nested containers or panels with custom styles to get the details just right. Alternatively you can just drop a full html snippet in there with all the data laid out like you want.
Would be happy to help more.
Fiddle: http://www.senchafiddle.com/#XQNA8
I have a json which has 3 main-menupoints. In the first menupoint, there is one submenu, so the menupoint 1 is no 'leaf'. When I click at the folder Symbol, the whole menu (the whole json file) is again under the submenu. So its infinity. Can someone help me with this please? THANK YOU!!
Here are my files:
app/data/tree.json
{
'result':
[
{
"text": "Point1",
'children':[
{
"text": "SubPoint1",
'leaf':true
},
]
},
{ "text": "Point2",
'leaf':true
},
{
"text": "Point3",
'leaf':true
},
]
}
view/TreeMenu.js
Ext.define('App.view.TreeMenu', {
extend: 'Ext.tree.Panel',
alias: 'widget.treemenu',
title:'Title',
store: 'TreeMenu'
});
store/TreeMenu.js
Ext.define('App.store.TreeMenu', {
extend: 'Ext.data.TreeStore',
requires: 'App.model.TreeMenu',
model: 'App.model.TreeMenu',
proxy: {
type: 'ajax',
url: 'app/data/tree.json',
reader: {
type: 'json',
root: 'result'
}
}
});
model/TreeMenu.js
Ext.define('App.model.TreeMenu', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: 'int', leaf:false, text:'name'},
{ name: 'value', type: 'string', leaf:false, text:'value'},
]
});
controller/TreeMenu.js
Ext.define('App.controller.TreeMenu', {
extend: 'Ext.app.Controller',
views: ['TreeMenu'],
models: ['TreeMenu'],
stores: ['TreeMenu'],
onLaunch: function() {
var treeStore = this.getTreeMenuStore();
treeStore.load({
//callback: this.onStationsLoad,
scope: this
});
},
refs: [{
selector: 'tree',
ref: 'treemenu'
}],
init: function() {
this.control({
'treemenu': {
itemclick : this.treeItemClick
}
});
},
treeItemClick : function(view, record) {
console.log(record);
}
});
It took me a while to realize but your tree is actually working as designed :)
I created a fiddle for it to visualize: http://jsfiddle.net/dbrin/auBTH/
Essentially every time you expand a node in a tree the default behavior is to load that node's children. That means the store will send a GET request to the server specified by the proxy url property. It sends the id of the node that you clicked on to expand so that the server side would use that id to run the query.
As you said you always return the same data set back and that is exactly what you are seeing: every time you expand a tree node an identical data set is appended to the tree as the children of the expanded node. One of the nodes you return is expandable .. and so you can repeat the process again :)
EDIT: upon further thought I found a separate issue that makes the node look for children nodes on the server instead of the already fetched sub nodes underneath. The issue is with how the reader is defined. In your original code you set json reader to parse data returned from the server specifying the root property set to result.
While this works on the initial load of data it also has an impact on how the children nodes are read. Essentially root config overrides the default children config. I corrected my previous example changing the root property to children - http://jsfiddle.net/dbrin/auBTH/4/ now it works as expected.
Do any children of Json tree has "leaf : true" ?
Maybe it's the problem...
-- EDIT:
I see that you have the leaf value.
Maybe can be this:
'children':[
{
"text": "SubPoint1",
'leaf':true
}-->, <------------------------- ERASE final comma.
]
I can confirm it helps. The trick is in json format
var data = {
"result": [{
"text": "Point1",
"children": [{
The problem is the array of nodes is firstly under "results" and next under "children". These must be the same and used in root configuration in proxy
var data = {
"result": [{
"text": "Point1",
"result": [{
root: 'result'
Michal
UPDATE:
here is my reader/store - not sure what to add to the reader to make this work
Ext.define('YT.store.Videos', {
extend: 'Ext.data.Store',
model: 'YT.model.Video',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'https://gdata.youtube.com/feeds/api/videos',
reader: {
type: 'json',
root: 'feed',
record: 'entry',
successProperty: 'success'
},
listeners: {
exception: function(store, response, app){
console.log('exception...');
console.log(response);
}
}
}
});
Here is my model:
Ext.define('YT.model.Video', {
extend: 'Ext.data.Model',
autoLoad: true,
fields: [
'title',
'published',
'content'
]
});
Here is a sample response:
{
version: '1.0',
encoding: 'UTF-8',
feed: {
junk: 'blahblahblahblah',
entry: [
title: {
$t: 'title'
},
content: {
encoding: 'flash/application',
src: 'http://youtube.com/watch?q=someCatVideo'
},
published: {
$t: '12-28-2012'
}
]
}
}
I'm not sure how to reconcile the two.
I've tried...
Ext.define('YT.model.Video', {
extend: 'Ext.data.Model',
autoLoad: true,
fields: [
{name: 'title', mapping: 'title.$t'},
{name: 'published', mapping: 'published.$t'},
{name: 'content', mapping: 'content.src'}
]
});
Bonus:
Definitely looking for tips on how to debug the implementation of these techniques, I'm rather new to JavaScript MVC.
You are on the right track with mapping, but you also need to define a reader where you specify where your records are in the JSON you get back - see official docs for good examples.
Incidentally, who thought $t was a good idea for a map key?
EDIT:
After your edits here is your working code:
http://jsfiddle.net/dbrin/mSJg3/
As far as debugging: the key for your issue was to clearly see the payload from the service (I used FireBug to inspect JSON object returned). Then mapping your Model class to fit the JSON object through mapping attribute and finally adjust JSON reader to let it know how to navigate your JSON payload (see my code example).
Once your exception listeners are not firing anymore (see code example for those again) that means you got your data into the store. To actually see the data I used Illuminations firebug plugin to inspect the data store. I saw only one record. What the heck? I observed id Property being set to some funky URL. This was happening by default as i did not specify an id attribute on the model. I resorted to spacifying idProperty to undefined to get around this funky behavior (see model code).
I used jsfiddle to quickly iterate through changes and running to see errors in the reader. Once I had no more errors I had jsfiddle show me the app I just build in such a way that I could use Illuminations plugin by using the show/light url: http://jsfiddle.net/dbrin/mSJg3/show/light/
This the model that I want to create using json file
Ext.define('Users', {
extend: 'Ext.data.Model',
fields: [{name: 'user_id', type: 'int'},
{name: 'user_name', type: 'string'}]
});
What do I have to do in order to automatically create this model, based on the content of a json response from the server?
In order to have the model created automatically, you need to include the metaData field with your Json data. metaData can be used to describe all of the fields for the Model.
In the ExtJS 4.1 documentation - Ext.data.reader.Json has a section called Response MetaData which describes basic use of this feature.
You should be able to pull down some json with fields and or some format that can be transformed into that format pretty easily.
Make call to service to get model's fields. Might need to define some chain that first calls model service and performs subsequent steps after.
Build model's field array w/ fields results from #1. May need to transform data based on response in #1.
var fields = response.fields;
Define model based on fields in Store's constructor
var store = Ext.create('Ext.data.Store', {
constructor: function () {
var model = Ext.define("Users", {
extend: "Ext.data.Model",
fields: fields
});
this.model = model.$className;
this.callParent(arguments);
}
});
I only use the jsonp, which loads an json file and parses it automatically, don't know if Ext.Ajax does this, too.
But you would do something like this:
definition.json:
{
"name": "User",
"fields": [
{ "name": "user_id" , "type": "int" },
{ "name": "user_name", "type": "string" }
]
}
load it:
Ext.Ajax.request({
url : "..../definition.json"
success: function( res ) {
Ext.define( res.name, {
extend: 'Ext.data.Model',
fields: res.fields
}, function() {
Ext.create( 'somestore', { model: res.name });
});
}
});