I am creating an app using "vue.js" in which there are few videos and there is a button, video title and video description for each video, so that when a user clicks on the button it will only display the related video, its title and the description but the video titles and descriptions of all other videos will be hidden. Now there are two ways to do this:
1. By creating the list of all the video titles and descriptions with default display property as none and then changing the display property to block of only one video title and description on the button click. For ex:
onBtnClick: function(event, index) {
var videoContainer = "#videoContainer" + index;
$(videoContainer).css("display", "block);
}
.container {
display: none;
}
<div v-for="(video, index) in json._videos" id="'videoContainer'+index" class="container">
<div v-if="video.title">{{ video.title }}</div>
<div v-if="video.decription">{{ video.decription }}</div>
</div>
<div id="buttonContainer">
<button v-for="(video, index) in json._videos" id="'button'+index" v-on:click="onBtnClick($event,index)">{{ video.buttonText }}</button>
</div>
By creating the single video container and then changing the html content of the video title and description using v-html directive. For ex:
onBtnClick: function(event, index) {
getVideoTitle(index);
getVideoDescription(index);
}
getVideoTitle: function(index) {
return json._videos[index].title;
}
getVideoDescription: function(index) {
return json._videos[index].description;
}
.container {
display: none;
}
<div id="'videoContainer'+index" class="container">
<div v-if="json._videos" v-html="getVideoTitle()"></div>
<div v-if="json._videos" v-html="getVideoDescription()"></div>
</div>
<div id="buttonContainer">
<button v-for="(video, index) in json._videos" id="'button'+index" v-on:click="onBtnClick($event,index)">{{ video.buttonText }}</button>
</div>
Guys please tell me which one is the better approach and why?
You can create two Vue components: VideoList and single Video.
In your Video component you should have attribute "isVisible" (or with other name, if you want), which will be set from VideoList component for the specifig Video component.
This approach grant you ability to separate your logic and gives full control for every single video and for all set of such videos.
Here is the full working example:
https://jsfiddle.net/yh1xrk27/22/
var VideoList = Vue.component('video-list', {
template: $('#video-list').html(),
data: function () {
return {
videos: [
]
};
},
mounted: function () {
this.videos = [
{
title: 'Video 1',
description: 'The first video',
isVisible: true
},
{
title: 'Video 2',
description: 'The second video',
isVisible: false
}
];
},
methods: {
show: function (index) {
var self = this;
for (var key in this.videos) {
this.videos[key].isVisible = (key == index);
}
}
}
});
var VideoItem = Vue.component('video-item', {
template: $('#video-item').html(),
props: [
'fields'
],
data: function () {
return {
title: '',
description: '',
isVisible: false
};
},
created: function () {
this.title = this.fields.title;
this.description = this.fields.description;
this.isVisible = this.fields.isVisible;
}
});
var demo = new Vue({
el: '#app'
});
Answer on author's comment:
You can get your date from any desired source, it's not the problem.
Also you do not have to define all fields for every single video. Every field in VideoItem have deafult value and when you assign attributes from property "fields", you should just check definition of every field. For example:
Replace this:
created: function () {
this.title = this.fields.title;
this.description = this.fields.description;
this.isVisible = this.fields.isVisible;
}
On this:
created: function () {
this.title = typeof this.fields.title !== 'undefined' ? this.fields.title : 'SOME_DEFAULT_VALUE';
this.description = typeof this.fields.title !== 'undefined' ? this.fields.description ? 'SOME_DEFAULT_VALUE';
this.isVisible = typeof this.fields.title ? this.fields.isVisible : false; // or true, depends on you
}
Or you can create separate method for setting data about videos and call this method in created hook of the component VideoList:
methods: {
...
setData: function(){
this.title = typeof this.fields.title !== 'undefined' ? this.fields.title : 'SOME_DEFAULT_VALUE';
this.description = typeof this.fields.title !== 'undefined' ? this.fields.description ? 'SOME_DEFAULT_VALUE';
this.isVisible = typeof this.fields.title ? this.fields.isVisible : false; // or true, depends on you
}
}
And in your VideoList component you can load information about videos from anywhere, from internal or external resource. For example:
created: function(){
var self = this;
$.ajax({
// load data from some internal resource or paste some external resource link
url: '/source/videos.json',
dataType: 'json',
success: function(jsonData){
// call setData method of this component
self.setData(jsonData);
},
});
}
So you don't have any limits about your JSON, do what you want with your raw JSON data and paste it in VideoList component, that's all.
I will suggest one more variable, use a data option of vue, and have a data variable for your videos. The benefit is that you can use the same variable at multiple places and it is reactive as well, i.e. change in that variable will be reflected in the view.
data:{
return videos: []
}
methods: {
onBtnClick: function(event, index) {
updateVideoTitle(index);
updateVideoDescription(index);
}
updateVideoTitle: function(index) {
this.videos[index] = Object.assign(this.videos[index], {title: json._videos[index].title});
}
updateVideoDescription: function(index) {
this.videos[index] = Object.assign(this.videos[index], {description: json._videos[index].description});
}
}
and in HTML:
<div v-for="(video, index) in videos" id="'videoContainer'+index" class="container">
<div v-if="video.title">{{ video.title }}</div>
<div v-if="video.decription">{{ video.decription }}</div>
</div>
<div id="buttonContainer">
<button v-for="(video, index) in videos" id="'button'+index" v-on:click="onBtnClick($event,index)">{{ video.buttonText }}</button>
</div>
I've just started developing with Angular schema form and I'm struggling to write any tests for my custom field directive.
I've tried compiling the schema form html tag which runs through my directives config testing it's display conditions against the data in the schema. However it never seems to run my controller and I can't get a reference to the directives HTML elements. Can someone give me some guidance on how to get a reference to the directive? Below is what I have so far:
angular.module('schemaForm').config(['schemaFormProvider',
'schemaFormDecoratorsProvider', 'sfPathProvider',
function(schemaFormProvider, schemaFormDecoratorsProvider, sfPathProvider) {
var date = function (name, schema, options) {
if (schema.type === 'string' && schema.format == 'date') {
var f = schemaFormProvider.stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'date';
options.lookup[sfPathProvider.stringify(options.path)] = f;
return f;
}
};
schemaFormProvider.defaults.string.unshift(date);
schemaFormDecoratorsProvider.addMapping('bootstrapDecorator', 'date',
'app/modules/json_schema_form/schema_form_date_picker/schema_form_date_picker.html');
}]);
var dateControllerFunction = function($scope) {
$scope.isCalendarOpen = false;
$scope.showCalendar = function () {
$scope.isCalendarOpen = true;
};
$scope.calendarSave = function (date) {
var leaf_model = $scope.ngModel[$scope.ngModel.length - 1];
var formattedDate = $scope.filter('date')(date, 'yyyy-MM-dd');
leaf_model.$setViewValue(formattedDate);
$scope.isCalendarOpen = false;
};
};
angular.module('schemaForm').directive('schemaFormDatePickerDirective', ['$filter', function($filter) {
return {
require: ['ngModel'],
restrict: 'A',
scope: false,
controller : ['$scope', dateControllerFunction],
link: function(scope, iElement, iAttrs, ngModelCtrl) {
scope.ngModel = ngModelCtrl;
scope.filter = $filter
}
};
}]);
<div ng-class="{'has-error': hasError()}">
<div ng-model="$$value$$" schema-form-date-picker-directive>
<md-input-container>
<!-- showTitle function is implemented by ASF -->
<label ng-show="showTitle()">{{form.title}}</label>
<input name="dateTimePicker" ng-model="$$value$$" ng-focus="showCalendar()" ng-disabled="isCalendarOpen">
</md-input-container>
<time-date-picker ng-model="catalogue.effectiveFrom" ng-if="isCalendarOpen" on-save="calendarSave($value)" display-mode="date"></time-date-picker>
</div>
<!-- hasError() defined by ASF -->
<span class="help-block" sf-message="form.description"></span>
</div>
And the spec:
'use strict'
describe('SchemaFormDatePicker', function() {
var $compile = undefined;
var $rootScope = undefined;
var $scope = undefined
var scope = undefined
var $httpBackend = undefined;
var elem = undefined;
var html = '<form sf-schema="schema" sf-form="form" sf-model="schemaModel"></form>';
var $templateCache = undefined;
var directive = undefined;
beforeEach(function(){
module('app');
});
beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_, _$httpBackend_) {
$compile = _$compile_
$rootScope = _$rootScope_
$httpBackend = _$httpBackend_
$templateCache = _$templateCache_
}));
beforeEach(function(){
//Absorb call for locale
$httpBackend.expectGET('assets/locale/en_gb.json').respond(200, {});
$templateCache.put('app/modules/json_schema_form/schema_form_date_picker/schema_form_date_picker.html', '');
$scope = $rootScope.$new()
$scope.schema = {
type: 'object',
properties: {
party: {
title: 'party',
type: 'string',
format: 'date'
}}};
$scope.form = [{key: 'party'}];
$scope.schemaModel = {};
});
describe("showCalendar", function () {
beforeEach(function(){
elem = $compile(html)($scope);
$scope.$digest();
$httpBackend.flush();
scope = elem.isolateScope();
});
it('should set isCalendarOpen to true', function(){
var result = elem.find('time-date-picker');
console.log("RESULT: "+result);
));
});
});
});
If you look at the below example taken from the project itself you can see that when it uses $compile it uses angular.element() first when setting tmpl.
Also, the supplied test module name is 'app' while the code sample has the module name 'schemaForm'. The examples in the 1.0.0 version of Angular Schema Form repo all use sinon and chai, I'm not sure what changes you would need to make if you do not use those.
Note: runSync(scope, tmpl); is a new addition for 1.0.0 given it is now run through async functions to process $ref includes.
/* eslint-disable quotes, no-var */
/* disabling quotes makes it easier to copy tests into the example app */
chai.should();
var runSync = function(scope, tmpl) {
var directiveScope = tmpl.isolateScope();
sinon.stub(directiveScope, 'resolveReferences', function(schema, form) {
directiveScope.render(schema, form);
});
scope.$apply();
};
describe('sf-array.directive.js', function() {
var exampleSchema;
var tmpl;
beforeEach(module('schemaForm'));
beforeEach(
module(function($sceProvider) {
$sceProvider.enabled(false);
exampleSchema = {
"type": "object",
"properties": {
"names": {
"type": "array",
"description": "foobar",
"items": {
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string",
"default": 6,
},
},
},
},
},
};
})
);
it('should not throw needless errors on validate [ノಠ益ಠ]ノ彡┻━┻', function(done) {
tmpl = angular.element(
'<form name="testform" sf-schema="schema" sf-form="form" sf-model="model" json="{{model | json}}"></form>'
);
inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.model = {};
scope.schema = exampleSchema;
scope.form = [ "*" ];
$compile(tmpl)(scope);
runSync(scope, tmpl);
tmpl.find('div.help-block').text().should.equal('foobar');
var add = tmpl.find('button').eq(1);
add.click();
$rootScope.$apply();
setTimeout(function() {
var errors = tmpl.find('.help-block');
errors.text().should.equal('foobar');
done();
}, 0);
});
});
});
I have a JsonResult method in my controller. I have pulled in data from my database and set to an object. The method will return this object. I am trying pass this data into AngularJS data source. I would like to display a DevExtreme bar chart. Here is code so far.
AngularJS file:
var app = angular.module('customApp', ['dx']);
app.controller("chartControl", function ($scope, $http) {
$scope.sizeSettings = {
dataSource: 'http://localhost:53640/Home/PostChart',
commonSeriesSettings: {
argumentField: 'product_id',
valueField: "product_id", name: "Product Cost",
type: "bar"
},
seriesTemplate: {
nameField: 'Source',
}
};
});
Home Controller:
public JsonResult PostChart(int product_id)
{
Object prod = null;
using (ProductOrderEntities db = new ProductOrderEntities())
{
var product = db.Products.FirstOrDefault(p => p.product_id == product_id);
prod = new {productID = product.product_id, productName = product.product_name, productPrice = product.product_cost, productDescription = product.product_type};
}
return Json(prod, JsonRequestBehavior.AllowGet);
}
}
HTML
<div ng-app="customApp">
<div ng-controller="chartControl">
</div>
</div>
Seems like you forget add markup for chart:
<div dx-chart="chartOptions"></div>
I've made a simple ASP.NET MVC application here https://www.dropbox.com/s/hk3viceoa2zkyng/DevExtremeChart.zip?dl=0
Open the start page http://localhost:56158/Default1/ to see chart in action.
See more information about using DevExtreme Charts in AngularJS app here http://js.devexpress.com/Documentation/Howto/Data_Visualization/Basics/Create_a_Widget/?version=14_2#Data_Visualization_Basics_Create_a_Widget_Add_a_Widget_AngularJS_Approach
This is how I solved it.
HTML
<div ng-app="customCharts">
<div ng-controller="ChartController">
<div dx-chart="productSettings"></div>
</div>
</div>
AngularJS
var app = angular.module('customCharts', ['dx']);
app.controller("ChartController", function ($scope, $http, $q) {
$scope.productSettings = {
dataSource: new DevExpress.data.DataSource({
load: function () {
var def = $.Deferred();
$http({
method: 'GET',
url: 'http://localhost:53640/Home/PostChart'
}).success(function (data) {
def.resolve(data);
});
return def.promise();
}
}),
series: {
title: 'Displays Product Costs for items in our Database',
argumentType: String,
argumentField: "Name",
valueField: "Cost",
type: "bar",
color: '#008B8B'
},
commonAxisSettings: {
visible: true,
color: 'black',
width: 2
},
argumentAxis: {
title: 'Items in Product Store Database'
},
valueAxis: {
title: 'Dollor Amount'
}
}
})
Controller
public JsonResult PostChart()
{
var prod = new List<Object>();
using (ProductOrderEntities db = new ProductOrderEntities())
{
var product = db.Products.ToList();
foreach (var p in product)
{
var thing = new { Name = p.product_name, Cost = p.product_cost };
prod.Add(thing);
}
}
return Json(prod, JsonRequestBehavior.AllowGet);
}
In an effort to learn more about Sencha Touch I am building a calendar webapp for our university. I have some scripts that work but the problem is when I try to update the listPanel with the JSON data from Events.php. I am getting the data from the page but the update command is not updating the listPanel and instead for some reason that I can't put my finger on is asking for the events variable which is commented out. Now if I uncomment that variable it will parse the JSON in that variable but for the life of me i have no idea why.
Here is the the index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Student Calendar</title>
<link rel="stylesheet" href="css/ext-touch.css" type="text/css"/>
<script type="text/javascript" src="js/ext-touch.js"></script>
<script type="text/javascript" src="js/calendar.js"></script>
<link rel="stylesheet" href="css/calendar.css" type="text/css"/>
</head>
<body>
<textarea id="eventList" class="x-hidden-display">
<ul id="eventsItems">
<tpl for=".">
<li class="date">
{date}
<li>
<tpl for="events">
<li>{title} - {location}</li>
</tpl>
</tpl>
</ul>
</textarea>
<textarea id="eventList" class="x-hidden-display">
<ul id="eventsDescriptionList">
<tpl for=".">
<li class="date">
{date}
<li>
<tpl for="events">
<li>{title} - {location}</li>
</tpl>
</ul>
</textarea>
<textarea id="eventDescription" class="x-hidden-display">
<tpl for=".">
<p>{title}</p>
<p>{description}</p>
<p>{location}</p>
<p>{startTime}</p>
</tpl>
</textarea>
</body>
</html>
Here is the calendar.js
Ext.setup({
onReady:function(){
eventListTemplate = Ext.XTemplate.from("eventList");
descriptionTemplate = Ext.XTemplate.from("eventDescription");
eventTapHandler = function(eventer,tapped){
tapID = tapped.id;
/*Pass Into The Ajax Portion for getting a events description*/
Ext.Ajax.request({
params:{
/*params are available inside of the Ajax request*/
needs:eventDescription,
panel:"descript",
id:tapID,
fails:rootPanel
},
url: 'Events.php',
method:'POST',
success: function(response, opts) {
/*Ext.uitl.JSON.decode() is used to change the response text to a JSON object*/
opts.params.needs.update(Ext.util.JSON.decode(response.responseText));
},
failure: function(response, opts) {
alert("Sorry There Was An Error");
opts.params.fails.setCard(0);
}
});
rootPanel.setCard(1);
}
eventBackHandler = function(){
rootPanel.setCard(0);
}
backButton = {
text:'Back',
ui:'back',
cls:'.backable',
handler:eventBackHandler
}
toolBarBack = new Ext.Toolbar({
dock:'top',
title:'Event Desciption',
cls:'toolBar',
items:[backButton],
});
listPanel = new Ext.Panel({
tpl:eventListTemplate,
scroll:'vertical',
tester:function()
{
alert('MAGIC');
},
getEvent:function(){
var currentPanel = this;
Ext.Ajax.request({
params:{
/*params are available inside of the Ajax request*/
panel:"list",
needs:currentPanel,
fails:rootPanel
},
url: 'Events.php',
method:'POST',
success: function(response, opts) {
var data = Ext.util.JSON.decode(response.responseText);
//opts.params.needs.tester();
/*Ext.uitl.JSON.decode() is used to change the response text to a JSON object*/
opts.params.needs.update(data);
},
failure: function(response, opts) {
alert("Sorry There Was An Error");
}
});
},
listeners: {el: {tap: eventTapHandler,delegate: '.touchable'}}
});
eventDescription = new Ext.Panel({
tpl:descriptionTemplate,
scroll:'vertical',
dockedItems:[toolBarBack]
});
rootPanel = new Ext.Panel({
fullscreen:true,
layout:"card",
animation: 'fade',
items:[listPanel,eventDescription]
});
//Offending Variable
//var events = [{id:1,title:'this is a title',location:'here and now',startTime:'Right behind you'},{id:2,title:'this is a title2',location:'here and now2',startTime:'Right behind you2'}]
//alert("done");
listPanel.getEvent();
}
});
Here is the events.php Sql and database names have been changed to protect the innocent
<?
$dbname = "magicalDatabase";
require_once("../../../db_setup.inc");
If($_POST['panel'] == "list" )
{
$currentMonth = date("m");
$currentYear = date("Y");
mysql_real_escape_string($currentMonth);
mysql_real_escape_string($currentYear);
$sql = "SELECT * FROM magicalcalendar WHERE calendar_start_year = '$currentYear' AND calendar_start_month = '$currentMonth' AND calendar_channel_guid = 'CurrentStudentsMain' ORDER BY calendar_start_month, calendar_start_day ASC";
$results = mysql_query($sql)or die(mysql_error()."------".__LINE__."-----$sql");
$dayCounts[01] = 31;
$dayCounts[02] = 29;
$dayCounts[03] = 31;
$dayCounts[04] = 30;
$dayCounts[05] = 31;
$dayCounts[06] = 30;
$dayCounts[07] = 31;
$dayCounts[08] = 31;
$dayCounts[09] = 30;
$dayCounts[10] = 31;
$dayCounts[11] = 30;
$dayCounts[12] = 31;
for($j=1;$j<$dayCounts[$currentMonth];$j++)
{
if($j<10)
{
$responce['0'.$j]['date'] = date(M)." - $j";
}
else{
$responce[$j]['date'] = date(M)." - $j";
}
}
while($event = mysql_fetch_array($results))
{
$responce[$event['calendar_start_day']]['events'][$event['calendar_id']]['id'] = $event['calendar_id'];
$responce[$event['calendar_start_day']]['events'][$event['calendar_id']]['title'] = $event['calendar_subject'];
$responce[$event['calendar_start_day']]['events'][$event['calendar_id']]['location'] = $event['calendar_location'];
$responce[$event['calendar_start_day']]['events'][$event['calendar_id']]['startTime'] = $event['calendar_start_month']."-".$event['calendar_start_day']."-".$event['calendar_start_year'];
}
echo json_encode($responce);
}
If($_POST['panel'] == "descript")
{
$id = $_POST['id'];
mysql_real_escape_string($id);
$sql = "SELECT * FROM magicalcalendar WHERE calendar_id = $id";
$results = mysql_query($sql)or die(mysql_error()."------".__LINE__."-----$sql");
$i=0;
while($event = mysql_fetch_array($results))
{
$responce['id'] = $event['calendar_id'];
$responce['description'] = $event['calendar_text'];
$responce['title'] = $event['calendar_subject'];
$responce['location'] = $event['calendar_location'];
$responce['startTime'] = $event['calendar_start_day']."-". $event['calendar_start_month']."-".$event['calendar_start_year'];
$i++;
}
echojson_encode($responce);
}
?>
Here is an example of the JSON data being sent
{
"05":{"events":{
"2856":{"id":"2856","title":"BSM Leadership Retreat","location":"Lake O' The Pines","startTime":"01-05-2011"}}},
"06":{"events":{
"2957":{"id":"2957","title":"Women's Basketball","location":"Brownwood, TX","startTime":"01-06-2011"},
"2958":{"id":"2958","title":"Men's Basketball","location":"Brownwood, TX","startTime":"01-06-2011"}}},
"08":{"events":{
"2959":{"id":"2959","title":"Women's Basketball","location":"Alpine, Tx","startTime":"01-08-2011"},
"2960":{"id":"2960","title":"Men's Basketball","location":"Alpine, TX","startTime":"01-08-2011"}}},
"11":{"events":{
"3052":{"id":"3052","title":"Theatre Auditions!","location":"Black Box Theatre","startTime":"01-11-2011"}}},
"12":{"events":{
"3054":{"id":"3054","title":"Welcome Back Party","location":"New Student Lounge","startTime":"01-12-2011"}}},
"13":{"events":{
"2961":{"id":"2961","title":"Women's Basketball","location":"Pineville, LA","startTime":"01-13-2011"},
"2962":{"id":"2962","title":"Men's Basketball","location":"Pineville, LA","startTime":"01-13-2011"}}},
"14":{"events":{
"3055":{"id":"3055","title":"Organizations Meeting","location":"Cornish Great room","startTime":"01-14-2011"}}},
"15":{"events":{
"2963":{"id":"2963","title":"Women's Basketball","location":"Clinton, MS","startTime":"01-15-2011"},
"2964":{"id":"2964","title":"Men's Basketball","location":"Clinton, MS","startTime":"01-15-2011"}}},
"20":{"events":{
"2965":{"id":"2965","title":"Women's Basketball","location":"ETBU\/Ornelas Gymnasium","startTime":"01-20-2011"},
"2966":{"id":"2966","title":"Men's Basketball","location":"Ornelas Gym\/ETBU","startTime":"01-20-2011"}}},
"21":{"events":{
"3056":{"id":"3056","title":"Karen Peck and New River","location":"Marshall Convention Center Auditorium","startTime":"01-21-2011"},
"3057":{"id":"3057","title":"Chamber Ensemble Recital","location":"Woods Great Room in OSC","startTime":"01-21-2011"}}},
"22":{"events":{
"2967":{"id":"2967","title":"Women's Basketball","location":"ETBU\/Ornelas Gymnasium","startTime":"01-22-2011"},
"2968":{"id":"2968","title":"Men's Basketball","location":"Ornelas Gym\/ETBU","startTime":"01-22-2011"}}},
"27":{"events":{
"2969":{"id":"2969","title":"Women's Basketball","location":"Clarksville, AR","startTime":"01-27-2011"},
"2970":{"id":"2970","title":"Men's Basketball","location":"Clarksville, AR","startTime":"01-27-2011"},
"3058":{"id":"3058","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-27-2011"}}},
"28":{"events":{
"2857":{"id":"2857","title":"ABIDE - A Reflective Prayer Conference","location":"TBD","startTime":"01-28-2011"},
"3059":{"id":"3059","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-28-2011"},
"3060":{"id":"3060","title":"ABIDE","location":"TBD","startTime":"01-28-2011"}}},
"29":{"events":{"2971":{"id":"2971","title":"Women's Basketball","location":"Richardson, TX","startTime":"01-29-2011"},
"2972":{"id":"2972","title":"Men's Basketball","location":"Richardson, TX","startTime":"01-29-2011"},
"3061":{"id":"3061","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-29-2011"},
"3062":{"id":"3062","title":"SAI Province Day","location":"JGMB and EDW","startTime":"01-29-2011"}}}
}
Also you can view the app here
It's best viewed in safari either mobile or desktop.
A much more effective way to bind, say, a list to a JSON data source is to use a Ext.data.Proxy which will take care of all the AJAX and asynchronous updating of the list.
I've taken the liberty of rewriting your app to demonstrate:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Scratch</title>
<script src="lib/touch/sencha-touch-debug.js" type="text/javascript"></script>
<link href="lib/touch/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />
<script>
Ext.setup({
onReady:function(){
//DATA
Ext.regModel('Event', {
fields: [
{name:'id'},
{name:'title'},
{name:'location'},
{name:'startTime'}
],
});
Ext.regStore('events', {
model: 'Event',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'events.json',
reader: {
type: 'json',
root: 'events'
}
},
getGroupString: function(record) {
return record.get('startTime');
}
});
//UI
eventListToolbar = new Ext.Toolbar({
title: 'Events'
});
eventList = new Ext.List({
store: 'events',
itemTpl: Ext.XTemplate.from("eventList"),
listeners: {
selectionchange: function (selectionModel, records) {
if (records[0]) {
eventDescription.update(records[0].data);
eventDescriptionToolbar.setTitle(records[0].get('title'))
rootPanel.setActiveItem(eventDescriptionPanel, {type:'slide'});
}
}
},
grouped:true
});
eventListPanel = new Ext.Panel({
dockedItems: [eventListToolbar],
items: [eventList]
});
eventDescriptionToolbar = new Ext.Toolbar({
items: {
text:'Back',
ui: 'back',
listeners: {
tap: function () {
rootPanel.setActiveItem(eventListPanel, {type:'slide', direction:'right'});
}
}
}
});
eventDescription = new Ext.Panel({
tpl: Ext.XTemplate.from("eventDescription"),
padding:20,
});
eventDescriptionPanel = new Ext.Panel({
dockedItems: [eventDescriptionToolbar],
items: [eventDescription],
});
rootPanel = new Ext.Panel({
fullscreen:true,
layout:"card",
items:[
eventListPanel,
eventDescriptionPanel
]
});
}
});
</script>
</head>
<body>
<textarea id="eventList" class="x-hidden-display">
{title} - {location}
</textarea>
<textarea id="eventDescription" class="x-hidden-display">
<p>{title}</p>
<p>{description}</p>
<p>{location}</p>
<p>{startTime}</p>
</textarea>
</body>
</html>
This consumes JSON that looks like this:
{"events":[
{"id":"2856","title":"BSM Leadership Retreat","location":"Lake O' The Pines","startTime":"01-05-2011"},
{"id":"2957","title":"Women's Basketball","location":"Brownwood, TX","startTime":"01-06-2011"},
{"id":"2958","title":"Men's Basketball","location":"Brownwood, TX","startTime":"01-06-2011"},
{"id":"2959","title":"Women's Basketball","location":"Alpine, Tx","startTime":"01-08-2011"},
{"id":"2960","title":"Men's Basketball","location":"Alpine, TX","startTime":"01-08-2011"},
{"id":"3052","title":"Theatre Auditions!","location":"Black Box Theatre","startTime":"01-11-2011"},
{"id":"3054","title":"Welcome Back Party","location":"New Student Lounge","startTime":"01-12-2011"},
{"id":"2961","title":"Women's Basketball","location":"Pineville, LA","startTime":"01-13-2011"},
{"id":"2962","title":"Men's Basketball","location":"Pineville, LA","startTime":"01-13-2011"},
{"id":"3055","title":"Organizations Meeting","location":"Cornish Great room","startTime":"01-14-2011"},
{"id":"2963","title":"Women's Basketball","location":"Clinton, MS","startTime":"01-15-2011"},
{"id":"2964","title":"Men's Basketball","location":"Clinton, MS","startTime":"01-15-2011"},
{"id":"2965","title":"Women's Basketball","location":"ETBU\/Ornelas Gymnasium","startTime":"01-20-2011"},
{"id":"2966","title":"Men's Basketball","location":"Ornelas Gym\/ETBU","startTime":"01-20-2011"},
{"id":"3056","title":"Karen Peck and New River","location":"Marshall Convention Center Auditorium","startTime":"01-21-2011"},
{"id":"3057","title":"Chamber Ensemble Recital","location":"Woods Great Room in OSC","startTime":"01-21-2011"},
{"id":"2967","title":"Women's Basketball","location":"ETBU\/Ornelas Gymnasium","startTime":"01-22-2011"},
{"id":"2968","title":"Men's Basketball","location":"Ornelas Gym\/ETBU","startTime":"01-22-2011"},
{"id":"2969","title":"Women's Basketball","location":"Clarksville, AR","startTime":"01-27-2011"},
{"id":"2970","title":"Men's Basketball","location":"Clarksville, AR","startTime":"01-27-2011"},
{"id":"3058","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-27-2011"},
{"id":"2857","title":"ABIDE - A Reflective Prayer Conference","location":"TBD","startTime":"01-28-2011"},
{"id":"3059","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-28-2011"},
{"id":"3060","title":"ABIDE","location":"TBD","startTime":"01-28-2011"},
{"id":"2971","title":"Women's Basketball","location":"Richardson, TX","startTime":"01-29-2011"},
{"id":"2972","title":"Men's Basketball","location":"Richardson, TX","startTime":"01-29-2011"},
{"id":"3061","title":"CASL Conference","location":"Ornelas Student Center","startTime":"01-29-2011"},
{"id":"3062","title":"SAI Province Day","location":"JGMB and EDW","startTime":"01-29-2011"}
]}
Slightly rough and ready. But it looks like: http://cl.ly/46dH & http://cl.ly/46Rl
Not technically an answer to your question. But does that work?