I'm trying to create a page that shows backlogs based on specific criteria but when i save or change view it's not automatically updated and I need to refresh the page can anyone help me? I'm new to app development for rally
Here is the code:
<!DOCTYPE html>
<html>
<head>
<title>UserStory Defect List</title>
<script type="text/javascript" src="https://rally1.rallydev.com/apps/2.1/sdk-debug.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('UserStory.Defect.CustomizableColumnsGridBoard', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
Ext.create('Rally.data.wsapi.TreeStoreBuilder').build({
models: ['defect', 'userstory'],
autoLoad: true,
enableHierarchy: true
}).then({
success: this._onStoreBuilt,
scope: this,
listeners: {
select: this._onSelect,
ready: this._onLoad,
scope: this
}
});
},
_onSelect: function() {
var grid = this.down('rallygridboardsharedviewcontrol'),
store = grid.getStore();
store.clearFilter(true);
store.filter(this._getStateFilter());
},
_onLoad: function() {
},
_onStoreBuilt: function(store) {
var modelNames = ['defect', 'userstory'],
context = this.getContext();
this.add({
xtype: 'rallygridboard',
context: context,
modelNames: modelNames,
toggleState: 'grid',
stateful: false,
plugins: [
'rallygridboardaddnew',
{
ptype: 'rallygridboardinlinefiltercontrol',
inlineFilterButtonConfig: {
stateful: true,
stateId: context.getScopedStateId('filters'),
modelNames: modelNames,
inlineFilterPanelConfig: {
quickFilterPanelConfig: {
defaultFields: [
'ArtifactSearch',
'Owner',
'ModelType',
'Tags'
],
addQuickFilterConfig: {
whiteListFields: ['Tags','Milestones']
}
},
advancedFilterPanelConfig:
{
advancedFilterRowsConfig: {
propertyFieldConfig: {
whiteListFields: ['Tags','Milestones']
}
}
}
}
}
},
{
ptype: 'rallygridboardfieldpicker',
headerPosition: 'left',
modelNames: modelNames,
stateful: true,
stateId: context.getScopedStateId('columns-example')
},
{
ptype: 'rallygridboardsharedviewcontrol',
sharedViewConfig: {
stateful: true,
stateId: context.getScopedStateId('custom-list-shared-view'),
enableUrlSharing: this.isFullPageApp !== false
}
},
{
ptype: 'rallygridboardactionsmenu',
menuItems: [
{
text: 'Export...',
handler: function() {
window.location = Rally.ui.gridboard.Export.buildCsvExportUrl(
this.down('rallygridboard').getGridOrBoard()
);
},
scope: this
}
],
buttonConfig: {
iconCls: 'icon-export'
}
},
'rallygridboardtoggleable'
],
cardBoardConfig: {
attribute: 'ScheduleState'
},
gridConfig: {
store: store,
columnCfgs: [
'Name',
'ScheduleState',
'State',
'Iteration',
'Release'
]
},
height: this.getHeight()-20
});
}
});
Rally.launchApp('UserStory.Defect.CustomizableColumnsGridBoard', {
name: 'UserStory Defect List'
});
});
rally.nLoad(function (){location.reload();});
</script>
<style type="text/css">
</style>
</head>
<body></body>
</html>
I changed the code to this which works but the views won't save the filtering.
<!DOCTYPE html>
<html>
<head>
<title>UserStory Defect List</title>
<script type="text/javascript" src="https://rally1.rallydev.com/apps/2.1/sdk-debug.js"></script>
<script type="text/javascript">
Rally.onReady(function () {
var Ext = window.Ext4 || window.Ext;
Ext.define('Rally.apps.backlog', {
extend: 'Rally.app.GridBoardApp',
alias: 'widget.backlogapp',
columnNames: ['FormattedID', 'Name', 'PlanEstimate', 'Priority', 'Owner'],
requires: [
'Rally.data.Ranker',
'Rally.data.wsapi.Filter',
'Rally.ui.gridboard.plugin.GridBoardInlineFilterControl',
'Rally.ui.gridboard.plugin.GridBoardSharedViewControl'
],
modelNames: ['hierarchicalrequirement', 'defect', 'defectsuite'],
statePrefix: 'backlog',
getAddNewConfig: function () {
var config = {};
if (this.getContext().isFeatureEnabled('S107862_TEAM_PLANNING_EXPANDED_BACKLOG_HOTNESS_PHASE_1')) {
config.margin = 0;
}
return _.merge(this.callParent(arguments), config);
},
getPermanentFilters: function (types) {
types = (types === undefined ? ['hierarchicalrequirement', 'defect', 'defectSuite'] : types);
var typeCriteria = [];
if (_.contains(types, 'defect')) {
typeCriteria.push(Rally.data.wsapi.Filter.and([
{ property: 'State', operator: '!=', value: 'Closed' },
{ property: 'TypeDefOid', operator: '=', value: this._getModelFor('defect').typeDefOid }
]));
}
if (_.contains(types, 'hierarchicalrequirement')) {
typeCriteria.push(Rally.data.wsapi.Filter.and([
{ property: 'DirectChildrenCount', operator: '=', value: 0 },
{ property: 'TypeDefOid', operator: '=', value: this._getModelFor('hierarchicalrequirement').typeDefOid }
]));
}
var defectSuiteModel = this._getModelFor('defectsuite');
return [
Rally.data.wsapi.Filter.and([
{ property: 'Release', operator: '=', value: null },
{ property: 'Iteration', operator: '=', value: null }
]),
Rally.data.wsapi.Filter.or(typeCriteria.concat(defectSuiteModel ? [{ property: 'TypeDefOid', operator: '=', value: defectSuiteModel.typeDefOid }] : []))
];
},
getGridConfig: function () {
return _.merge(this.callParent(arguments), {
inlineAddConfig: {
listeners: {
beforeeditorshow: function (addNewCmp, params) {
params.Iteration = 'u'; // explicitly set iteration to unscheduled so it doesn't default to current iteration on TPS editor.
}
}
}
});
},
getGridStoreConfig: function () {
return {
enableHierarchy: false
};
},
getGridBoardCustomFilterControlConfig: function () {
var context = this.getContext();
var blackListFields = ['Iteration', 'PortfolioItem', 'Release'];
var whiteListFields = ['Milestones', 'Tags'];
if (context.isFeatureEnabled('S107862_TEAM_PLANNING_EXPANDED_BACKLOG_HOTNESS_PHASE_1')) {
return {
ptype: 'rallygridboardinlinefiltercontrol',
inlineFilterButtonConfig: {
stateful: true,
stateId: context.getScopedStateId('backlog-inline-filter'),
filterChildren: true,
modelNames: this.modelNames,
inlineFilterPanelConfig: {
quickFilterPanelConfig: {
defaultFields: [
'ArtifactSearch',
'Owner',
'ModelType'
],
addQuickFilterConfig: {
blackListFields: blackListFields,
whiteListFields: whiteListFields
}
},
advancedFilterPanelConfig: {
advancedFilterRowsConfig: {
propertyFieldConfig: {
blackListFields: blackListFields,
whiteListFields: whiteListFields
}
}
}
}
}
};
}
return {
showOwnerFilter: false,
showIdFilter: true,
idFilterConfig: {
stateful: true,
stateId: this.getScopedStateId('backlog-id-filter'),
storeConfig: {
autoLoad: true,
pageSize: 25,
fetch: ['FormattedID', '_refObjectName'],
filters: this.getPermanentFilters()
}
}
};
},
getSharedViewConfig: function() {
var context = this.getContext();
if (true) {
return {
ptype: 'rallygridboardsharedviewcontrol',
sharedViewConfig: {
stateful: true,
stateId: context.getScopedStateId('backlog-shared-view'),
defaultViews: _.map(this._getDefaultViews(), function(view) {
Ext.apply(view, {
Value: Ext.JSON.encode(view.Value, true)
});
return view;
}, this),
enableUrlSharing: this.isFullPageApp !== false
},
enableGridEditing: context.isFeatureEnabled('S91174_ISP_SHARED_VIEWS_MAKE_PREFERENCE_NAMES_UPDATABLE')
};
}
return {};
},
_getDefaultViews: function() {
var rankColumnDataIndex = this.getContext().getWorkspace().WorkspaceConfiguration.DragDropRankingEnabled ? Rally.data.Ranker.RANK_FIELDS.DND : Rally.data.Ranker.RANK_FIELDS.MANUAL;
return [
{
Name: 'Default View',
identifier: 1,
Value: {
toggleState: 'grid',
columns: _.flatten([
{ dataIndex: rankColumnDataIndex },
_.map(this.columnNames, function(columnName) {
return { dataIndex: columnName }
})
]),
sorters:[{ property: rankColumnDataIndex, direction: 'ASC' }]
}
}
];
},
getGridBoardConfig: function () {
var config = this.callParent(arguments);
return _.merge(config, {
listeners: {
viewchange: function() {
this.loadGridBoard();
},
scope: this
}
});
},
_getModelFor: function(type) {
return _.find(this.models, { typePath: type });
},
onFilterTypesChange: function(types) {
this.gridboard.gridConfig.storeConfig.filters = this.getPermanentFilters(types);
}
});
Rally.launchApp('Rally.apps.backlog', {
name: 'UserStory Defect List'
});
});
</script>
<style type="text/css">
</style>
</head>
<body></body>
</html>
Related
I have this code to show bar-chart with VueJS:
Vue.component('bar-chart', {
extends: VueChartJs.Bar,
data: function () {
return {
datacollection: {
labels: ['MICROFINANZAS -SECTOR COMERCIO','MICROFINANZAS -SECTOR SERVICIOS'],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
pointBackgroundColor: 'white',
borderWidth: 1,
pointBorderColor: '#249EBF',
data: [15000, 71700]
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: true
}
}],
xAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: false
}
}]
},
legend: {
display: false
},
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function (tooltipItems, data) {
return '$' + tooltipItems.yLabel;
}
}
},
responsive: true,
maintainAspectRatio: false,
height: 200
}
}
},
mounted() {
// this.chartData is created in the mixin
this.renderChart(this.datacollection, this.options)
}
})
Method in VueJS
var app = new Vue({
el: '#grid',
data: {
columns: ['id', 'nombre'],
objeto: "",
searchQuery: "",
dataChart: "",
dataChart1: "",
},
created: function () {
this.getDeudas();
},
methods: {
getDeudas: function () {
this.$http.get(baseURL + "/Home/ConsultarDeudasHome").then(function (response) {
this.lista = response.data.data;
console.log(this.lista);
this.objeto = JSON.parse(this.lista);
console.log(this.objeto[1].original);
this.dataChart = [this.objeto[0].original, this.objeto[0].actual];
console.log(this.dataChart);
this.dataChart1 = [this.objeto[1].original, this.objeto[1].actual];
});
},
},
This code show this bar chart:
But I need replace in my code two variables dynamic:
labels: ['MICROFINANZAS -SECTOR COMERCIO','MICROFINANZAS -SECTOR SERVICIOS'],
data: [15000, 71700]
With the information of method getDeudas()
How can to made this action?
This is the solution, I use props and watch:
Vue.use(VueTables.ClientTable);
Vue.component("bar-chart", {
extends: VueChartJs.Bar,
props: ["data", "options"],
mounted() {
this.renderLineChart();
},
computed: {
chartData: function () {
return this.data;
}
},
methods: {
renderLineChart: function () {
this.renderChart(
{
labels: ["Sector Comercio", "Sector Servicios"],
datasets: [
{
label: "Consolidado",
backgroundColor: "#f87979",
data: this.chartData
},
],
},
{ responsive: true, maintainAspectRatio: false }
);
}
},
watch: {
data: function () {
this.renderLineChart();
}
}
});
const baseURL = window.location.protocol + "//" + window.location.host;
var app = new Vue({
el: '#grid',
data: {
columns: ['id', 'nombre'],
objeto: "",
dataChart: "",
},
created: function () {
this.getDeudas();
},
methods: {
getDeudas: function () {
this.$http.get(baseURL + "/Home/ConsultarDeudasHome").then(function (response) {
this.lista = response.data.data;
this.objeto = JSON.parse(this.lista);
this.dataChart = [this.objeto[0].original, this.objeto[1].original];
});
},
},
})
How to toggle the customizing_column_menu icons in angular ui.grid like shown below
I didn't found any best solution to change menuItems dynamically, Here is what I did for workaround
Code
$scope.gridOptions = {
rowHeight: 75,
columnDefs: [{
field: 'name',
enableColumnMenu: false
}, {
field: 'gender',
enableHiding: false,
suppressRemoveSort: true,
sort: {
direction: uiGridConstants.ASC
}
}, {
field: 'All Details',
cellTemplate: '<p ng-show="grid.appScope.company"><label><b>Company:</b></label><span>{{row.entity.company}}</span></p><p ng-show="grid.appScope.email"><label><b>email:</b></label><span>{{row.entity.email}}</span></p>',
menuItems: [{
title: 'Company',
icon: 'ui-grid-icon-ok',
action: function() {
$scope.company = $scope.company ? false : true;
},
context: $scope,
shown: function() {
return this.context.company;
}
},{
title: 'Company',
icon: 'ui-grid-icon-cancel',
action: function() {
$scope.company = $scope.company ? false : true;
},
context: $scope,
shown: function() {
return !this.context.company;
}
}, {
title: 'Email',
icon: 'ui-grid-icon-ok',
action: function() {
$scope.email = $scope.email ? false : true;
},
context: $scope,
shown: function() {
return this.context.email;
}
}, {
title: 'Email',
icon: 'ui-grid-icon-cancel',
action: function() {
$scope.email = $scope.email ? false : true;
},
context: $scope,
shown: function() {
return !this.context.email;
}
}]
}]
};
Working Plunkr
In the code below I am trying to copy a existing feature, and for that creating a new object as DeepCopy function is not working for me. But formattedId is not getting generated for new feature object
Rally.onReady(function() {
var newObj = {};
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
autoScroll: true,
launch: function() {
Ext.create('Rally.ui.dialog.ChooserDialog', {
//model: 'PortfolioItem/Feature',
//fetch: ['FormattedID','Name','UserStories'],
width: 450,
autoScroll: true,
height: 525,
title: 'Select to Copy',
pageSize: 100,
closable: false,
selectionButtonText: 'Copy',
//autoLoad: true,
artifactTypes: ['portfolioitem'],
autoShow: true,
listeners: {
artifactChosen: function(selectedRecord) {
newObj = selectedRecord;
this.onqModelRetrieved();
},
scope: this
},
storeConfig : {
filters: [
{
property: 'PortfolioItemType.Name',
operator: '!=',
value: ''
}
]
}
});
},
onqModelRetrieved: function() {
Rally.data.ModelFactory.getModel({
type: 'PortfolioItem',
success: this.onModelRetrieved,
scope: this
});
},
onModelRetrieved: function(model) {
this.model = model;
this.createFeature();
},
createFeature: function() {
var record = Ext.create(this.model, {
Name: "(Copy of) " + newObj.data.Name,
//State: 'Open',
Description: newObj.data.Description,
type: newObj.data.Workspace.type
});
record.save;
}
});
Rally.launchApp('CustomApp', {
name: 'Example'
});
});
Please any suggestion, any help on this..
Per WS API documentation, PortfolioItem is a non-creatable type. With some modifications, here is your code that creates a feature. Below are two examples.
I replaced portfolioitem with portfolioitem/feature in the artifactTypes of the ChooserDialog in the first example.
The second example allows a choice of pi types, and notice that the type in Rally.data.ModelFactory.getModel in the second example is set dynamically.
EXAMPLE 1 (only features):
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:{ html:'App SDK 2.0rc3 Docs'},
_newObj : {},
launch: function() {
Ext.create('Rally.ui.dialog.ChooserDialog', {
width: 450,
autoScroll: true,
height: 525,
title: 'Select to Copy',
pageSize: 100,
closable: false,
selectionButtonText: 'Copy',
artifactTypes: ['portfolioitem/feature'],
autoShow: true,
listeners: {
artifactChosen: function(selectedRecord) {
console.log(selectedRecord.get('FormattedID') + ', ' + selectedRecord.get('Name') + ' was chosen');
this._newObj = selectedRecord;
this.onqModelRetrieved();
},
scope: this
},
});
},
onqModelRetrieved: function() {
Rally.data.ModelFactory.getModel({
type: 'PortfolioItem/Feature',
success: this.onModelRetrieved,
scope: this
});
},
onModelRetrieved: function(model) {
this.model = model;
this.createFeature();
},
createFeature: function() {
var record = Ext.create(this.model, {
Name: "(Copy of) " + this._newObj.get('Name'),
});
record.save({
callback: function(result, operation) {
if(operation.wasSuccessful()) {
console.log('created feature:', result.get('ObjectID'),result.get('FormattedID'),result.get('Name'));
}
else{
console.log("error");
}
}
});
}
});
EXAMPLE 2 (all pi types):
The second example works with all pi types, and artifactTypes is expanded to include themes, initiative and features:
artifactTypes: ['portfolioitem/theme','portfolioitem/initiative','portfolioitem/feature']
Here is the code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:{ html:'App SDK 2.0rc3 Docs'},
_newObj : {},
_type : null,
launch: function() {
Ext.create('Rally.ui.dialog.ChooserDialog', {
width: 450,
autoScroll: true,
height: 525,
title: 'Select to Copy',
pageSize: 100,
closable: false,
selectionButtonText: 'Copy',
artifactTypes: ['portfolioitem/theme','portfolioitem/initiative','portfolioitem/feature'],
autoShow: true,
storeConfig:{
fetch: ['Name','PortfolioItemTypeName']
},
listeners: {
artifactChosen: function(selectedRecord) {
console.log(selectedRecord.get('FormattedID') + ', ' + selectedRecord.get('Name') + ' of type ' + selectedRecord.get('PortfolioItemTypeName') + ' was chosen');
this._type = selectedRecord.get('PortfolioItemTypeName');
this._newObj = selectedRecord;
this.onqModelRetrieved();
},
scope: this
},
});
},
onqModelRetrieved: function() {
var that = this;
that._type = 'PortfolioItem/' + that._type,
Rally.data.ModelFactory.getModel({
type: that._type,
success: this.onModelRetrieved,
scope: this
});
},
onModelRetrieved: function(model) {
this.model = model;
this.createFeature();
},
createFeature: function() {
var record = Ext.create(this.model, {
Name: "(Copy of) " + this._newObj.get('Name'),
});
record.save({
callback: function(result, operation) {
if(operation.wasSuccessful()) {
console.log('created feature:', result.get('ObjectID'),result.get('FormattedID'),result.get('Name'),result.get('PortfolioItemTypeName'));
}
else{
console.log("error");
}
}
});
}
});
Using AppSDK 2.0, how can I see the "Release" field in the PortfolioItem/Feature when I create a store?
Release needs to be among the fetched fields in WsapiDataStore:
fetch: ['FormattedID','Name','UserStories','Release']
But then in a Rally.data.custom.Store, to account for Features that are not assigned to a release, this condition is used:
Release: (release && release.Name) || 'None'
Here is the code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
Ext.create('Rally.data.WsapiDataStore', {
model: 'PortfolioItem/Feature',
fetch: ['FormattedID','Name','UserStories','Release'],
pageSize: 100,
autoLoad: true,
listeners: {
load: this._onDataLoaded,
scope: this
}
});
},
_createGrid: function(features) {
this.add({
xtype: 'rallygrid',
store: Ext.create('Rally.data.custom.Store', {
data: features,
pageSize: 100
}),
columnCfgs: [
{
text: 'Formatted ID', dataIndex: 'FormattedID', xtype: 'templatecolumn',
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate')
},
{
text: 'Name', dataIndex: 'Name'
},
{
text: 'Release', dataIndex: 'Release'
},
{
text: 'Story Count', dataIndex: 'StoryCount'
},
{
text: 'User Stories', dataIndex: 'UserStories',
renderer: function(value) {
var html = [];
Ext.Array.each(value, function(userstory){
html.push('' + userstory.FormattedID + '')
});
return html.join(', ');
}
}
]
});
},
_onDataLoaded: function(store, data){
var features = [];
var pendingstories = data.length;
//debugger;
Ext.Array.each(data, function(feature) {
var release = feature.get('Release');
var f = {
FormattedID: feature.get('FormattedID'),
Name: feature.get('Name'),
Release: (release && release.Name) || 'None',
_ref: feature.get("_ref"),
StoryCount: feature.get('UserStories').Count,
UserStories: []
};
var stories = feature.getCollection('UserStories');
stories.load({
fetch: ['FormattedID'],
callback: function(records, operation, success){
Ext.Array.each(records, function(story){
var number = story.get('DirectChildrenCount');
if (number == 0) {
f.UserStories.push({_ref: story.get('_ref'),
FormattedID: story.get('FormattedID')
});}
}, this);
--pendingstories;
if (pendingstories === 0) {
this._createGrid(features);
}
},
scope: this
});
features.push(f);
}, this);
}
});
please I'm trying to get a list of all the contacts on my phone with the following code.
var App = new Ext.Application({
name: 'SmsthingyApp',
useLoadMask: true,
launch: function () {
Ext.data.ProxyMgr.registerType("contactstorage",
Ext.extend(Ext.data.Proxy, {
create: function(operation, callback, scope) {
},
read: function(operation, callback, scope) {
},
update: function(operation, callback, scope) {
},
destroy: function(operation, callback, scope) {
}
})
);
Ext.regModel("contact", {
fields: [
{name: "id", type: "int"},
{name: "givenName", type: "string"},
{name: "familyName", type: "string"},
{name: "emails", type: "auto"},
{name: "phoneNumbers", type: "auto"}
]
});
Ext.regStore('contacts',{
model: "contact",
proxy: {
type: "contactstorage",
read: function(operation, callback, scope) {
var thisProxy = this;
navigator.contacts.find(
['id', 'name', 'emails', 'phoneNumbers', 'addresses'],
function(deviceContacts) {
//loop over deviceContacts and create Contact model instances
var contacts = [];
for (var i = 0; i < deviceContacts.length; i++) {
var deviceContact = deviceContacts[ i ];
var contact = new thisProxy.model({
id: deviceContact.id,
givenName: deviceContact.name.givenName,
familyName: deviceContact.name.familyName,
emails: deviceContact.emails,
phoneNumbers: deviceContact.phoneNumbers
});
contact.deviceContact = deviceContact;
contacts.push(contact);
}
//return model instances in a result set
operation.resultSet = new Ext.data.ResultSet({
records: contacts,
total : contacts.length,
loaded : true
});
//announce success
operation.setSuccessful();
operation.setCompleted();
//finish with callback
if (typeof callback == "function") {
callback.call(scope || thisProxy, operation);
}
},
function (e) { console.log('Error fetching contacts'); },
{multiple: true}
);
}
}
});
Ext.regModel('Sms', {
idProperty: 'id',
fields: [
{ name: 'id', type: 'int' },
{ name: 'date', type: 'date', dateFormat: 'c' },
{ name: 'title', type: 'string' },
{ name: 'message', type: 'string' }
],
validations: [
{ type: 'presence', field: 'id' },
{ type: 'presence', field: 'title', message: 'Please select a contact for this sms.' }
]
});
Ext.regStore('SmsStore', {
model: 'Sms',
sorters: [{
property: 'date',
direction: 'DESC'
}],
proxy: {
type: 'localstorage',
id: 'sms-app-localstore'
},
getGroupString: function (record)
{
if (record && record.data.date)
{
return record.get('date').toDateString();
}
else
{
return '';
}
}
});
SmsthingyApp.views.ContactsList = new Ext.List({
id: 'ContactsList',
layout: 'fit',
store:'contacts',
itemTpl: '{givenName} {familyName}',
listeners: {'render': function (thisComponent)
{
SmsthingyApp.views.ContactsList.getStore().load();
}
},
onItemDisclosure: function (record) {
//Ext.dispatch({
// controller: SmsthingyApp.controllers.contacts,
// action: 'show',
// id: record.getId()
//});
}
});
SmsthingyApp.views.contactsListContainer = new Ext.Panel({
id: 'contactsListContainer',
layout: 'fit',
html: 'This is the sms list container',
items: [SmsthingyApp.views.ContactsList],
dockedItems: [{
xtype: 'toolbar',
title: 'Contacts'
}]
});
SmsthingyApp.views.smsEditorTopToolbar = new Ext.Toolbar({
title: 'Edit SMS',
items: [
{
text: 'Back',
ui: 'back',
handler: function () {
SmsthingyApp.views.viewport.setActiveItem('smsListContainer', { type: 'slide', direction: 'right' });
}
},
{ xtype: 'spacer' },
{
text: 'Save',
ui: 'action',
handler: function () {
var smsEditor = SmsthingyApp.views.smsEditor;
var currentSms = smsEditor.getRecord();
// Update the note with the values in the form fields.
smsEditor.updateRecord(currentSms);
var errors = currentSms.validate();
if (!errors.isValid())
{
currentSms.reject();
Ext.Msg.alert('Wait!', errors.getByField('title')[0].message, Ext.emptyFn);
return;
}
var smsList = SmsthingyApp.views.smsList;
var smsStore = smsList.getStore();
if (smsStore.findRecord('id', currentSms.data.id) === null)
{
smsStore.add(currentSms);
}
else
{
currentSms.setDirty();
}
smsStore.sync();
smsStore.sort([{ property: 'date', direction: 'DESC'}]);
smsList.refresh();
SmsthingyApp.views.viewport.setActiveItem('smsListContainer', { type: 'slide', direction: 'right' });
}
}
]
});
SmsthingyApp.views.smsEditorBottomToolbar = new Ext.Toolbar({
dock: 'bottom',
items: [
{ xtype: 'spacer' },
{
text: 'Send',
handler: function () {
// TODO: Send current sms.
}
}
]
});
SmsthingyApp.views.smsEditor = new Ext.form.FormPanel({
id: 'smsEditor',
items: [
{
xtype: 'textfield',
name: 'title',
label: 'To',
required: true
},
{
xtype: 'textareafield',
name: 'narrative',
label: 'Message'
}
],
dockedItems:[
SmsthingyApp.views.smsEditorTopToolbar,
SmsthingyApp.views.smsEditorBottomToolbar
]
});
SmsthingyApp.views.smsList = new Ext.List({
id: 'smsList',
store: 'SmsStore',
grouped: true,
emptyText: '<div style="margin: 5px;">No notes cached.</div>',
onItemDisclosure: function (record)
{
var selectedSms = record;
SmsthingyApp.views.smsEditor.load(selectedSms);
SmsthingyApp.views.viewport.setActiveItem('smsEditor', { type: 'slide', direction: 'left' });
},
itemTpl: '<div class="list-item-title">{title}</div>' +'<div class="list-item-narrative">{narrative}</div>',
listeners: {'render': function (thisComponent)
{
thisComponent.getStore().load();
}
}
});
SmsthingyApp.views.smsListToolbar = new Ext.Toolbar({
id: 'smsListToolbar',
title: 'Sent SMS',
layout: 'hbox',
items:[
{xtype:'spacer'},
{
id:'newSmsButton',
text:'New SMS',
ui:'action',
handler:function()
{
var now = new Date();
var smsId = now.getTime();
var sms = Ext.ModelMgr.create({ id: smsId, date: now, title: '', narrative: '' },'Sms');
SmsthingyApp.views.smsEditor.load(sms);
SmsthingyApp.views.viewport.setActiveItem('smsEditor', {type: 'slide', direction: 'left'});
}
}
]
});
SmsthingyApp.views.smsListContainer = new Ext.Panel({
id: 'smsListContainer',
layout: 'fit',
html: 'This is the sms list container',
dockedItems: [SmsthingyApp.views.smsListToolbar],
items: [SmsthingyApp.views.smsList]
});
SmsthingyApp.views.viewport = new Ext.Panel({
fullscreen: true,
layout: 'card',
cardAnimation: 'slide',
items:[
SmsthingyApp.views.contactsListContainer,
SmsthingyApp.views.smsListContainer,
SmsthingyApp.views.smsEditor
]
});
}
})
I'm using eclipse and LogCat tab keeps marking this red
02-08 11:11:58.741: E/Web Console(13886): Uncaught TypeError: Cannot read property 'contacts' of undefined at file:///android_asset/www/app.js:35
I'm guessing this has something to with why I can't see the contacts in the contactsListContainer.
Any help please?
I'm not a Sencha expert but I do know that this line:
var App = new Ext.Application({
will cause problems with PhoneGap as we also declare a variable called App. It would be better to change that line to be something like:
var myApp = new Ext.Application({
to avoid the name conflict.
If that doesn't resolve your problem I suggest you read over my post on searching contacts. I'd make sure I could successfully search for contacts before adding in Sencha.