Sencha Touch searchable list -- new view with data proxy - json

Two quick questions here... How can I use this example
http://try.sencha.com/touch/2.0.0/examples/list-search/
of a searchable list, but opened in a NEW view? The example has it defined as the main application in app.js, but I would like to use it in "FirstApp.view.searchlist"
I know the answer is pretty easy but I am still a young grasshoppa and need a push in the right direction.
Also, rather than pulling the data from the embedded store like the example, I would like to modify it to pull my data from my external/proxy JSON store, which is defined as follows:
Store:
Ext.define('FirstApp.store.StudentStore',{
extend:'Ext.data.Store',
config:{
autoLoad:true,
model:'FirstApp.model.people',
sorters: 'lastName',
proxy:{
type:'ajax',
url:'http://xxxyyyzzz.com/data/dummy_data.json',
reader:{
type:'json',
rootProperty:'results'
}
}
}
});
Model:
Ext.define('FirstApp.model.people', {
extend: 'Ext.data.Model',
config: {
fields: ['firstName', 'lastName' , 'image','status', 'phone','rank','attendance', 'discipline','recent']
}
});
So, how can I turn that example into a "view" inside my application, with my data store and model?
Any help is greatly appreciated! Thank you!
Jake
-----------UPDATE-------------
Ok fantastic. I was able to implement the search feature (stoked) by combining your methods with another tutorial I found. Now one more question...Seems so easy but it is tough! How can I open my new 'Details' view once an item is selected/clicked ??
Search list:
Ext.define('FirstApp.view.MainPanel', {
extend: 'Ext.dataview.List',
alias : 'widget.mainPanel',
config: {
store : 'Students',
itemTpl:
'<h1>{firstName:ellipsis(45} {lastName:ellipsis(45)}</h1>' ,
itemCls:'place-entry',
items: [
{
xtype: 'toolbar',
docked: 'top',
items: [
{
xtype: 'searchfield',
placeHolder: 'Search People...',
itemId: 'searchBox'
}
]
}
]
}
});
Details view (that I want to open when name is clicked from Search list/Mainpanel view):
Ext.define('FirstApp.view.Details',{
extend:'Ext.Panel',
xtype:'details',
config:{
layout:'fit',
tpl:'<div class="image_container"><img src="{image}"></div>' +
'<h1>{firstName:ellipsis(25)} {lastName:ellipsis(25)}</h1>'+
'<div class="status_container">{status:ellipsis(25)}</div> '+
'<div class="glance_container"> <div class="value_box"><div class="value_number"> {rank:ellipsis(25)}</div> <p class="box_name">Rank</p> </div> <div class="value_box"><div class="value_number"> {attendance:ellipsis(25)}</div> <p class="box_name" style="margin-left: -10px;">Attendance</p> </div> <div class="value_box"><div class="value_number">{discipline:ellipsis(25)}</div> <p class="box_name" style="margin-left: -4px;">Discipline</p> </div> <div class="value_box"><div class="value_number"> {recent:ellipsis(25)}</div> <p class="box_name">Recent</p> </div> </div> '+
'<h2>Phone:</h2> <div class="phone_num"><p>{phone:ellipsis(25)}</p></div>'+
'<h3>Some info:</h3><p>Round all corners by a specific amount, defaults to value of $default-border-radius. When two values are passed, the first is the horizontal radius and the second is the vertical radius.</p>',
scrollable:true,
styleHtmlContent:true,
styleHtmlCls:'details'
}
})
Search Controller:
Ext.define('FirstApp.controller.SearchController', {
extend : 'Ext.app.Controller',
config: {
profile: Ext.os.deviceType.toLowerCase(),
stores : ['StudentStore'],
models : ['people'],
refs: {
myContainer: 'MainPanel',
placesContainer:'placesContainer'
},
control: {
'mainPanel': {
activate: 'onActivate'
},
'mainPanel searchfield[itemId=searchBox]' : {
clearicontap : 'onClearSearch',
keyup: 'onSearchKeyUp'
},
'placesContainer places list':{
itemtap:'onItemTap'
}
}
},
onActivate: function() {
console.log('Main container is active');
},
onSearchKeyUp: function(searchField) {
queryString = searchField.getValue();
console.log(this,'Please search by: ' + queryString);
var store = Ext.getStore('Students');
store.clearFilter();
if(queryString){
var thisRegEx = new RegExp(queryString, "i");
store.filterBy(function(record) {
if (thisRegEx.test(record.get('firstName')) ||
thisRegEx.test(record.get('lastName'))) {
return true;
};
return false;
});
}
},
onClearSearch: function() {
console.log('Clear icon is tapped');
var store = Ext.getStore('Students');
store.clearFilter();
},
init: function() {
console.log('Controller initialized');
},
onItemTap:function(list,index,target,record){ // <-----NOT WORKING
this.getPlacesContainer().push({
xtype:'details',
store:'Students',
title:record.data.name,
data:record.data
})
}
});

Good question. I assume you are trying to build a List or dataview. The key here is to give your store a 'storeId'. I have modified your store below:
Ext.define('FirstApp.store.StudentStore',{
extend:'Ext.data.Store',
config:{
storeId: 'Students', // Important for view binding and global ref
autoLoad:true,
model:'FirstApp.model.people',
sorters: 'lastName',
proxy:{
type:'ajax',
url:'http://xxxyyyzzz.com/data/dummy_data.json',
reader:{
type:'json',
rootProperty:'results'
}
}
}
});
Then inside your view, you reference the store to bind to. Here is an example List view from one of my applications. Notice the config object has 'store' which references our above store:
Ext.define('app.view.careplan.CarePlanTasks', {
extend: 'Ext.dataview.List',
xtype: 'careplanTasks',
requires: [
'app.view.template.CarePlan'
],
config: {
store: 'Students', // Important! Binds this view to your store
emptyText: 'No tasks to display',
itemTpl: Ext.create('app.view.template.CarePlan'),
},
constructor : function(config) {
console.log('CarePlan List');
this.callParent([config]);
}
});
Now that you have a storeId, you can access this store anywhere in your application by doing the following:
Ext.getStore('Students')
You can load records from your server by calling the load method as well:
Ext.getStore('Students').load();
You can do this anywhere in your application, but typically it's best to do in your controllers.
Hope this helps.
======= Updates to your updates ======
So looking at your code I think you need to modify your List view and the controller. Give 'FirstApp.view.MainPanel' an xtype: 'MainPanel'. Next modify your controller config as follows:
config: {
profile: Ext.os.deviceType.toLowerCase(),
stores : ['StudentStore'],
models : ['people'],
refs: {
mainPanel: 'MainPanel', // set the object KEY to the name you want to use in the control object and set the VALUE to the xtype of the view
placesContainer:'placesContainer'
},
control: {
'mainPanel': { // this matches the key above, which points to your view's xtype
activate: 'onActivate',
itemtap: 'onItemTap' // listen for the item tap event on this List
},
'mainPanel searchfield[itemId=searchBox]' : {
clearicontap : 'onClearSearch',
keyup: 'onSearchKeyUp'
},
'placesContainer places list':{
itemtap:'onItemTap'
}
}
},

Related

How to use treeview with angular ui grid

Ho guys, i'm using angular ui grid in my project..heres my code :
$scope.gridOptions = {
enableSorting: false,
enableRowSelection: true,
multiSelect: false,
exporterMenuCsv: false,
// enableGridMenu: true,
enableSelectAll: true,
enableColumnResizing: true,
enableSorting: true,
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi; //set gridApi on scope
gridApi.core.on.renderingComplete($scope, function () {
$timeout(function () {
var gridBodyElem = document.getElementById(gridApi.grid.id + '-grid-container');
gridBodyElem.addEventListener('mouseup', handleGridClick);
});
});
},
columnDefs: [
{ field: 'name',displayName:'Name',cellClass: 'gridField'},
{ field: 'uniqueid',displayName:'ID',cellClass: 'gridField'},
{ field: 'devicetime',displayName:'GPS DATE',cellClass: 'gridField'},
{ field: 'adress',displayName:'Adresse',cellClass: 'gridField'},
{ field: 'company',displayName:'Company',cellClass: 'gridField'}
]
};
it works good but i want to use a treeview in this grid to obtain like this table grid below..
what i want exacly is, when i click on item, i want to show a html code below like the image above...is there anyone has any idea how to do this..??
Example controller :
var SomeController = function () {
this.customHtml = '<ul><li>render me please</li></ul>';
}
Example view :
<div ng:bind="customHtml"></div>
Gives :
<div>
"<ul><li>render me please</li></ul>"
</div>

typeahead / filter / JSON parse?

Trying to 'parse/read' an external .json file on my typeahead code, but the .json file (which I cannot modify) looks like:
{"**cms_countries**":
[{"**cms_country**":
[{"**countrydisplayname**":"Afghanistan"}
,{"countrydisplayname":"Albania"} ,{"countrydisplayname":"Algeria"}
... ... ... ,{"countrydisplayname":"Zimbabwe"} ] } ,{"TotalRecords":
[ {"TotalRecords":"246"} ] } ] }
So, I think my problem is to know how to parse/read/assimilate/integrate/adopt this .json file, having
cms_countries ,
cms_country ,
and then, my countrydisplayname field on it. (have you seen the tree here ?)
This is my code:
$(document).ready(function() {
var searchablePlaces = new Bloodhound({
datumTokenizer : Bloodhound.tokenizers.obj.whitespace("countrydisplayname"),
queryTokenizer : Bloodhound.tokenizers.whitespace,
prefetch : 'countries.json',
remote : {
url : 'countries/%QUERY.json',
wildcard : '%QUERY',
filter : function(response) { return response.cms_country; }
},
limit : 10
});
searchablePlaces.initialize();
$('#remote .typeahead').typeahead(
{
hint : true,
highlight : true,
minLength : 2
},
{
name : 'countrydisplayname',
displayKey : "countrydisplayname",
source : searchablePlaces.ttAdapter()
})
});
But of course, it is not working:
ANY hint on how to organize my filter... ? or how to do to overcome my nested .json wrappers....
OK, I've got my code working now:
$(window).load(function(){
var movies = new Bloodhound({
limit: 10,
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'countries.json',
filter: function (movies) {
return $.map(movies.cms_countries[0].cms_country, function (paises) {
return {
value: paises.countrydisplayname
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
movies.initialize();
// Instantiate the Typeahead UI
$('.typeahead').typeahead(
{
hint: true,
highlight: true,
minLength: 1
},
{
//displayKey: 'value',
displayKey: function (toto) {
return toto.value;
},
source: movies.ttAdapter()
});
});

Populate model of the model with sails js

I'm trying to populate model of the model with sails unfortunally it doesn't work.
I have 3 models
/**
Conversation.js
**/
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
tableName:'conversation',
attributes: {
idConversation:{
columnName:'IDCONVERSATION',
primaryKey:true,
autoIncrement:true,
unique:true,
type:'integer',
index:true
},
dateStartConversation:{
columnName:'DATEDEBUT',
type:'date',
index:true
},
user1:{
columnName:'IDUSER1',
model:'user',
notNull:true
},
user2:{
columnName:'IDUSER2',
model:'user',
notNull:true
},
article:
{
model:'article',
columnName:'IDARTICLE',
notNull:true
}
}
};
/**
Article.js
**/
module.exports = {
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false,
tableName:'article',
attributes: {
idArticle:{
type:'integer',
unique:true,
columnName:'IDARTICLE',
autoIncrement:true,
primaryKey:true
},
title:{
type:'string',
required:true,
columnName:'TITRE',
index:true,
notNull:true
},
utilisateur:{
model:'utilisateur',
columnName:'IDUTILISATEUR',
required:true,
notNull:true,
dominant:true
},
images:{
collection:'image',
via:'article'
},
conversation:{
collection:'conversation',
via:'article'
}
}
};
/**
Image.js
**/
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
tableName:'image',
attributes: {
idImage:{
columnName:'IDIMAGE',
primaryKey:true,
autoIncrement:true,
unique:true,
type:'integer'
},
pathImage:{
columnName:'PATHIMAGE',
required:true,
type:'string',
notNull:true
},
article:{
model:'article',
columnName:'IDARTICLE',
notNull:true,
dominant:true
}
}
};
As you can see in my model, an conversation its between Two user, about one article, and those article cas have one or many Images.
So I want to get all conversations of one user and I able to populate with article but I'm not able to populate article with Image below how I proceed
Conversation.find().populate('article').populate('user1').populate('user2').where({
or : [
{ user1: iduser },
{ user2: iduser }
]})
.then(function( conversations) {
var i=0;
conversations.forEach(function(element,index){
i++;
console.log("article "+index+" "+JSON.stringify(element.article));
Article.findOne({
idArticle:element.article.idArticle
}).populate('images').then(function(newArticle){
//I try to set article with the newArticle but it don't work
element.article=newArticle;
})
if(i==conversations.length){
res.json({
hasConversation:true,
conversation:conversations
});
}
});
})
Because deep populate is not possible using sails, I try to use a loop to populate each article with associate Images and set it in conversation, But article is never set in conversation.
How can I fix it ?
Judging by the if(i==conversations.length) at the end, you seem to have an inkling that you need to write asynchronous code. But you're iterating i inside of the synchronous forEach loop, so your response is happening before any of the database queries even run. Move the i++ and the if inside of the callback for Article.findOne:
Conversation.find().populate('article').populate('user1').populate('user2').where({
or : [
{ user1: iduser },
{ user2: iduser }
]})
.then(function( conversations) {
var i=0;
conversations.forEach(function(element,index){
console.log("article "+index+" "+JSON.stringify(element.article));
Article.findOne({
idArticle:element.article.idArticle
}).populate('images').then(function(newArticle){
// Associate the article with the conversation,
// calling `toObject` on it first
element.article= newArticle.toObject();
// Done processing this conversation
i++;
// If we're done processing ALL of the conversations, send the response
if(i==conversations.length){
res.json({
hasConversation:true,
conversation:conversations
});
}
})
});
})
You'll also need to call toObject on the newArticle instance before assigning it to the conversation, because it contains getters and setters on the images property which behave unexpectedly when copied.
I'd also recommend refactoring this to use async.each, which will make it more readable.
Until this is resolved (https://github.com/balderdashy/sails-mongo/issues/108), you can use this function that I developed to solve this: https://gist.github.com/dinana/52453ecb00d469bb7f12

How to add a tooltip based on a DropDown list

Basically I have a Kendo Grid where a couple of my columns also have Kendo DropDowns.
I would like to attach a ToolTip based on the value the user chooses from the "Instrument" dropdown.
Here's my grid javascript code (using MVVM pattern):
tradesGrid = $("#tradesGrid").kendoGrid({
dataSource: datasource,
toolbar: [
{ name: "create", text: "Add Trade" }
],
columns: [{
field: "TradeId"
},
{
field: "Instrument",
editor: instrumentsDropDownEditor, template: "#=Instrument#"
},
{ command: ["edit", "destroy"] },
],
sortable: true,
editable: "popup",
});
and here's the Editor function for the Instrument dropdown :
function instrumentsDropDownEditor(container, options) {
var instrItems = [{
"optionInstr": "OPTION 22/11/2013 C70 Equity"
}, {
"optionInstr": "OPTION 26/11/2013 C55 Equity"
},
{
"optionInstr": "OPTION 30/11/2013 C80 Equity"
}
];
var input = $('<input id="Instrument" name="Instrument">');
input.appendTo(container);
input.kendoDropDownList({
dataTextField: "optionInstr",
dataValueField: "optionInstr",
dataSource: instrItems, // bind it to the brands array
optionLabel: "Choose an instrument"
}).appendTo(container);
}
and on my Html view file I'm starting with this idea :
<span class="key-button"
title="Instrument Details!!!"
data-role="tooltip"
data-auto-hide="true"
data-position="right"
data-bind="events: { show: onShow, hide: onHide }"
</span>
I don't have time to rig up a jsFiddle right now, but I'm pretty sure this would work...
Init your tooltip in javascript, and specify content that is a function:
var myTooltip = "";
var tooltipWidget = $("#whatever").kendoTooltip({
filter: "a",
content: function (item) { return myTooltip; },
...
}).data("kendoTooltip");
Then on your dropdown widget, specify a change function handler that sets myTooltip to whatever contents you want.
var onSelect = function (e) {
myTooltip = ...;
tooltipWidget.refresh();
};
$("#dropdownlist").kendoDropDownList({
select: onSelect,
...
});
Or you can change the title property on your HTML element, if that is easier. However, the tooltip widget won't refresh its contents after it has been displayed, which is why you need to manually call tooltipWidget.refresh() after you change the contents.

How to show nested items in Sencha Touch list

I have following json data.
{
"pendingTasksVOs" : [{
"groupName" : "LAST_MONTH",
"documents" : [{
"description" : "kvk1",
"uploadDate" : "1-12-2012"
}
]
}, {
"groupName" : "OLDER",
"documents" : [{
"description" : "kvk2",
"uploadDate" : "1-11-2012"
}, {
"description" : "kvk3",
"uploadDate" : "1-10-2012"
}, {
"description" : "kvk4",
"uploadDate" : "1-01-2012"
}
]
}
]
}
I want to show it in Sencha Touch list grouped by GroupName .
I want list to be in following format:
GoupName : Group1
-----------------------------
Description11 , UploadDate11 This should be separate clickable list row
-----------------------------
Description12 , UploadDate12 This should be separate clickable list row
-----------------------------
Description13 , UploadDate13 This should be separate clickable list row
-----------------------------
******************************
GoupName : Group2
-----------------------------
Description21 , UploadDate21 This should be separate clickable list row
-----------------------------
Description22 , UploadDate22 This should be separate clickable list row
-----------------------------
******************************
I am trying to use Sencha Touch List component. But I am not to get the list as per above requirements
//This is my store
var store = Ext.create('Ext.data.Store', {
model: 'Demo.model.DocumentList',
rootProperty: 'pendingTasksVOs',
autoLoad: true,
grouper: {
groupFn: function(record) {
if (record && record.data.title) {
return record.get('groupName');
} else {
return '';
}
}
}
});
//template for list item
var listTemplate = new Ext.XTemplate(
'<tpl for=".">',
'{groupName}</br>',
'<ul>',
'<tpl for="documents">',
'{description} {uploadDate} </br>',
'</tpl>',
'</ul>',
'</tpl>'
);
// Initialize the main view
Ext.getCmp("pendingTasksListId").add(Ext.create('Ext.List', {
fullscreen: true,
itemTpl: listTemplate,
store: store,
groupField:'description',
grouped: true
}));
//DocumentListModel is defined under /app/model/
Ext.define('Demo.model.DocumentList', {
extend: 'Ext.data.Model',
requires : ['Demo.model.Doc'],
config: {
fields: ['groupName', 'documents'],
hasMany : {
model : "Demo.model.Doc",
associationKey: 'documents'
},
proxy: {
type: 'rest',
url : "/getPendingTasks",
reader: {
type: 'json',
rootProperty : 'pendingTasksVOs'
}
}
}
}
//Document Model is defined under /app/model/
Ext.define('Demo.model.Doc', {
extend: 'Ext.data.Model',
config: {
fields: ['description', 'uploadDate'],
belongsTo: "Demo.model.DocumentList"
}
});
});
I am able to get the list item as follows:
GroupName,
Description11,UploadDate11
Description12,UploadDate12
But above whole content is clickable. I want to have each Description & UploadDate pair to be clickable.
Can anyone guide me on how can I achieve above stated requirement?
I got the solution for my question.
Create following custom json reader under /app/reader/ folder as CustomReader.js
Ext.define('Demo.reader.CustomReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.customReader',
getData: function(data) { // overriding
data = this.callParent(arguments);
var responseString = Ext.encode(data);
var json = JSON.parse(responseString);
var result = [];
Ext.each(json.pendingTasksVOs, function(entry) {
var groupName = entry.groupName;
Ext.each(entry.documents || [], function(document) {
result.push({
description : document.description,
uploadDate: document.uploadDate,
groupName: groupName
});
});
});
return result;
}
});
Doc model should be created at /app/model as follows as Doc.js
Ext.define('Demo.model.Doc', {
extend: 'Ext.data.Model',
config: {
fields: ['description','uploadDate','groupName']
}
});
Store can be created at /app/store folder
Store should use this custom reader as follows:
Ext.define("Demo.store.DocumentStore", {
extend:'Ext.data.Store',
requires:['Demo.model.Doc' , 'Ext.data.proxy.Rest', 'Demo.reader.CustomReader'],
id:'DocumentStore1',
config:{
model:'Demo.model.Doc',
autoLoad:true,
grouper:{
groupFn:function (record) {
if (record && record.data.groupName) {
return record.get('groupName');
} else {
return '';
}
}
},
proxy:{
type:'rest',
url:"/getPendingTasks",
reader:{
type:'customReader' //This is our custom reader
}
}
}
});
//Finally You can create the list as follows
Ext.create('Demo.store.DocumentStore');
// Initialize the main view
Ext.getCmp("pendingTasksListId").add(Ext.create('Ext.List', {
fullscreen:true,
itemTpl:'<div class="contact">{description} {uploadDate} {groupName} </div>',
store: 'DocumentStore1',
grouped:true
}));
Above gives me the list where I get separate row for each {description} {uploadDate} pair from the received json specified in question
Try this,
itemTpl: [
'<div>GroupName : {groupName}</div>',
'<div>',
'<ul>',
'<tpl for="documents">',
'<li>{description} , {uploadDate}</li>',
'</tpl>',
'</ul>',
'</div>',
].join('')
OR this,
itemTpl: [
'<div>GroupName : {groupName}</div>',
'<div>',
'<ul>',
'<tpl for="documents">',
'<li><div onclick="whateverYouWantToDo()">{description} , {uploadDate}</div></li>',
'</tpl>',
'</ul>',
'</div>',
].join('')