ExtJS Ext.data.JsonStore loadData - extjs

Can you help me solve the issue I'm running into with the loadData function as part of the Ext.data.JsonStore? I've created a basic example of the problem I'm running into:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Ext JSON example</title>
<script type="text/javascript" src="lib/ext-base.js"></script>
<script type="text/javascript" src="lib/ext-all.js"></script>
<script>
function example() {
var exampleData = "{'exampleJSON' : {'exampleArray':[{'exampleID':1,'name':'Fred','description':'a guy'},{'exampleID':2,'name':'sue','description':'a girl'}]}}";
var exampleStore = new Ext.data.JsonStore({
data: new Ext.data.MemoryProxy(exampleData),
autoLoad: false,
root: 'exampleJSON.exampleArray',
fields: [
{mapping: "exampleID", name: 'exampleID'},
{mapping: "name", name: 'name'},
{mapping: "description", name: 'description'}
],
listener: {
load: function (oStore, ayRecords, oOptions )
{
alert('loaded successfully');
}
}
});
exampleStore.loadData(exampleData);
}
</script>
</head>
<body>
<center><button onclick="example();">Click for Example</button></center>
</body>
</html>
The problem I'm running into is I'm getting this error reported by Firebug:
obj.exampleJSON is undefined
This is likely being caused when I set the root as 'exampleJSON.exampleArray'.
Can someone help point out what I'm doing wrong?
(using ExtJs 4.1.0)
Thanks guys.
EDIT: to set this up, place ext-all.js and ext-base.js in a lib folder.

Your code is wrong in a number of places:
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: ['exampleID', 'name', 'description']
});
function example() {
var exampleData = [{
exampleID: 1,
name: 'Fred',
description: 'a guy'
}, {
exampleID: 2,
name: 'sue',
description: 'a girl'
}];
var exampleStore = new Ext.data.Store({
model: 'MyModel',
data: exampleData
});
}
Also, there's no ext-base file for Ext 4, so it's a redundant include.

Thanks for your replies, they were useful in sending me down the right path. I was able to get my original example to work by removing the 'data' field. I'm guessing it was causing conflicts when I tried to call loadData. Solution listed beflow
function example() {
var exampleData = {'exampleJSON' : {'exampleArray':[{'exampleID':1,'name':'Fred','description':'a guy'},{'exampleID':2,'name':'sue','description':'a girl'}]}};
var exampleStore = new Ext.data.JsonStore({
autoLoad: false,
root: "exampleJSON.exampleArray",
fields: [
{mapping: "exampleID", name:"exampleID"},
{mapping: "name", name:"name"},
{mapping: "description", name:"description"}
],
listeners: {
load: function (oStore, ayRecords, oOptions )
{
alert('loaded successfully: ' + ayRecords.length);
}
}
});
exampleStore.loadData(exampleData);
}

Related

Extjs testing framework(jasmine + extjs?)

Are there any good examples of ExtJs framework with Jasmine or any other testing framework? I am using ExtJs 6.7 and 7.1
I found this thread on Sencha forums, but I think the topic is outdated and the thread seems dead.
https://www.sencha.com/forum/showthread.php?308318
I have some success with siesta-lite(https://www.npmjs.com/package/siesta-lite) but I am looking for a free alternative, to run it on a CI process.
Thanks!
Here's my directory structure generated when running this command
sencha -sdk <path-to-ext-framework> generate app MyApp ./extjs-jasmine
extjs-jasmine/
...
app/
store/
...
Personnel.js
...
...
index.html
...
test.html
test/
lib/
jasmine-3.5.0/ (just download and extract latest jasmine here)
app-test.js
spec/
store/
TestPersonnelStore.js
Here's test.html
<!DOCTYPE HTML>
<html manifest="">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Jasmine Unit Tests</title>
<link rel="stylesheet" href="test/lib/jasmine-3.5.0/jasmine.css">
<script src="ext/ext-bootstrap.js"></script>
<script src="test/lib/jasmine-3.5.0/jasmine.js"></script>
<script src="test/lib/jasmine-3.5.0/jasmine-html.js"></script>
<script src="test/lib/jasmine-3.5.0/boot.js"></script>
<script src="test/lib/app-test.js"></script>
<script src="test/spec/store/TestPersonnelStore.js"></script>
</head>
<body>
</body>
</html>
Here's test/lib/app-test.js
Ext.Loader.setConfig({
enabled: true,
disableCaching: false,
paths: {
//examples to map unconventional packages
'Ext.ux': 'ext/packages/ux/src',
'Ext.chart': 'ext/packages/charts/src/chart',
'Ext.chart.legend.LegendBase': 'ext/packages/charts/classic/src/chart/legend/LegendBase.js',
'Ext.chart.theme.BaseTheme': 'ext/packages/charts/classic/src/chart/theme/BaseTheme.js',
'Ext.draw': 'ext/packages/charts/src/draw',
'Ext.draw.ContainerBase': 'ext/packages/charts/classic/src/draw/ContainerBase.js',
'Ext.draw.SurfaceBase': 'ext/packages/charts/classic/src/draw/SurfaceBase.js',
//test spec namespace
'MyApp.spec': 'test/spec',
}
});
Ext.application({
name: 'MyApp',
autoCreateViewport: false
});
Here's app/store/Personnel.js
Ext.define('MyApp.store.Personnel', {
extend: 'Ext.data.Store',
alias: 'store.personnel',
fields: [
'name', 'email', 'phone'
],
data: { items: [
{ name: 'Jean Luc', email: "jeanluc.picard#enterprise.com", phone: "555-111-1111" },
{ name: 'Worf', email: "worf.moghsson#enterprise.com", phone: "555-222-2222" },
{ name: 'Deanna', email: "deanna.troi#enterprise.com", phone: "555-333-3333" },
{ name: 'Data', email: "mr.data#enterprise.com", phone: "555-444-4444" }
]},
proxy: {
type: 'memory',
reader: {
type: 'json',
rootProperty: 'items'
}
}
});
Here's test/spec/store/TestPersonnelStore.js
describe('MyApp.spec.store.TestPersonnelStore', () => {
beforeAll((done) => {
Ext.require([
'MyApp.store.Personnel'
], () => {
done();
});
});
it('should load data...', (done) => {
const store = Ext.create('MyApp.store.Personnel', {
autoLoad: true
});
expect(store.getCount()).toBe(4);
done();
});
});
Then point your browser to http://<host>:<port>/<context>/test.html

UI grid not displaying data Angular

I do have a UI grid which displays the Group Name.
$scope.gridOptions = {
enableSorting : false,
columnDefs: [
{ name:'GroupName' ,enableCellEdit:false}
],
data: [
{ 'GroupName' : groupData}
]
};
For the data in UI grid , i am passing an Object array in the form of :
groupData = [{"GroupName": "Mathematicians"}{"GroupName":"Scientist"}]
But am not getting anything in the UI grid.
Thanks in advance
Some observations :
Your $scope.groupData is not having a valid JSON.
It should be $scope.groupData = [{"GroupName": "Mathematicians"},{"GroupName":"Scientist"}]
Your gridOptions object should be like this.
$scope.gridOptions = {
data: 'groupData',
enableSorting : false,
columnDefs: [{
field: 'GroupName',
displayName: 'Group Name',
name:'GroupName',
enableCellEdit:false
}]
};
DEMO
var app = angular.module('uigrid', ['ngTouch', 'ui.grid']);
app.controller('MainCtrl', ['$scope', function ($scope) {
$scope.gridOptions = {
data: 'groupData',
enableSorting : false,
columnDefs: [{
field: 'GroupName',
displayName: 'Group Name',
name:'GroupName',
enableCellEdit:false
}]
};
$scope.groupData = [{"GroupName": "Mathematicians"},{"GroupName":"Scientist"}]
}]);
</style> <!-- remove this, it is just for jsfiddle -->
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/angular-ui/bower-ui-grid/master/ui-grid.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-touch.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-animate.js"></script>
<script src="https://cdn.rawgit.com/angular-ui/bower-ui-grid/master/ui-grid.min.js"></script>
<style>
.grid {
width: 500px;
height: 250px;
}
<div ng-app="uigrid">
<div ng-controller="MainCtrl">
<div ui-grid="gridOptions" class="grid"></div>
</div>
</div>
You are nesting one level too deep when passing the data.
Instead of (the equivalent of):
data: [ {
GroupName: [
{ GroupName: 'Mathematicians' },
{ GroupName: 'Scientist' }
]
} ]
you just want to pass all the data in the data property, so you get:
data: groupData

Bar Chart with Rally.data.lookback.calculator.TimeSeriesCalculator

I need to create bar chart showing up various values, such as "Created", "Closed", "Submitted" data with count on Y Axis and dates on x axis.
I am able to do this successfully, for any one criteria. However I am not able to get multiple bars being shown.
Below is the code I am using currently:
<!DOCTYPE html>
<html>
<head>
<title>Defect Trend App</title>
<script type="text/javascript" src="/apps/2.0rc1/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function () {
(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
return this.createTrendChart();
},
createTrendChart: function() {
var ProjectOid;
ProjectOid = this.getContext().getProject().ObjectID;
var ReleaseOID = <My Release ID>;
Ext.define('My.TrendCalc', {
extend: 'Rally.data.lookback.calculator.TimeSeriesCalculator',
getMetrics: function() {
return [
{
as: 'Defects',
display: 'column',
f: 'count'
}
];
}
});
this.myTrendChart = Ext.create('Rally.ui.chart.Chart', {
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: {
find: {
_TypeHierarchy: "Defect",
State: {
$lt: "Closed"
},
_ProjectHierarchy: ProjectOid,
Release: ReleaseOID
},
fetch: ["_ValidFrom", "_ValidTo", "ObjectID"]
},
calculatorType: 'My.TrendCalc',
calculatorConfig: {},
chartConfig: {
chart: {
zoomType: 'x',
type: 'column'
},
title: {
text: 'Defects over Time'
},
xAxis: {
type: 'datetime',
minTickInterval: 1
},
yAxis: {
title: {
text: 'Number of Defects'
}
}
}
});
return this.add(this.myTrendChart);
}
});
}).call(this);
Rally.launchApp('CustomApp', {
name:"Defect Trend App",
parentRepos:""
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>
I do not consider myself an expert in this area, but I believe this will work for you...
I took your code as a base and modified it based on some other code to get what I think appears to be a working version of what you want. Below is a screenshot of the code running in Rally.
The data I had did not have a lot of variance in the series (most were released) so it looks uninteresting.
You will probably want to exclude the final state (as I believe you did in your code via the $lt:'Completed'... which i changed to $lte:'Completed' temporarily).
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html><head><title>
Defect Trend App </title>
<script type="text/javascript" src="/apps/2.0rc1/sdk.js"></script>
<script type="text/javascript">
var states = ['Accepted','Released']; // all enum values for 'State'
var field = 'ScheduleState'; // or 'State'
var ReleaseOID = XXXXXX; // your release Oid
Rally.onReady(function () {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
return this.createTrendChart();
},
createTrendChart: function() {
var ProjectOid;
ProjectOid = this.getContext().getProject().ObjectID;
Ext.define('My.TrendCalc', {
extend: 'Rally.data.lookback.calculator.TimeSeriesCalculator',
getDerivedFieldsOnInput: function() {
var m = _.map(states, function(state) {
return {
"as": state,
"f" : function(snapshot) {
var value = (snapshot[field] == state) ? 1 : 0;
return value;
}
}
})
return m;
},
getMetrics : function() {
var m = _.map(states, function(state) {
return {
field: state,
as: state,
f: 'sum'
}
})
return m;
}
});
this.myTrendChart = Ext.create('Rally.ui.chart.Chart', {
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: {
find: {
_TypeHierarchy: "Defect",
State: {$lte: "Closed" },
_ProjectHierarchy: ProjectOid,
Release: ReleaseOID
},
fetch: ["_ValidFrom", "_ValidTo", "ObjectID", field],
hydrate: [field],
sort: { "_ValidFrom" : 1}
},
calculatorType: 'My.TrendCalc',
calculatorConfig : {},
chartConfig: {
chart: {
zoomType: 'xy',
type:'column'
},
title: {
text: 'Defects over Time'
},
xAxis: {
type: 'datetime',
title: { text: 'When'},
minTickInterval: 5,
labels : { rotation: 90 }
},
yAxis: { title: { text: 'Count' } },
plotOptions: {
series: {
stacking: 'normal'
}
}
}
});
return this.add(this.myTrendChart);
}
});
});
console.log("launching application");
Rally.launchApp('CustomApp', {
name:'Defect Trend App',
parentRepos:""
});
</script>
</head>
<body>
</body>
Pastebin - http://pastebin.com/Vf6jniGZ
I'm not familiar with Rally's App SDK wrappers, but I'm the primary author of Lumenize where the TimeSeriesCalculator comes from. Your situation is exactly what the group-by functionality in Lumenize.TimeSeriesCalculator was designed for. See the documentation for a careful walkthrough of how the TimeSeriesCalculator works. Look at the second example titled, "Time-series group-by example". Also, here is a functioning jsfiddle of that group-by example.
The key bit that you need is:
metrics = [
...
{f: 'groupByCount', groupByField: 'ScheduleState', allowedValues: allowedValues, prefix: 'Count '},
...
]

How to hide column in ng grid

here is my code:
index.html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link rel="stylesheet" type="text/css" href="http://angular-ui.github.com/ng- grid/css/ng-grid.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
<script type="text/javascript" src="http://angular-ui.github.com/ng-grid/lib/ng-grid.debug.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MyCtrl">
<div class="gridStyle" ng-grid="gridOptions"></div>
<div class="selectedItems">Selected ID:{{mySelections[0].id}}</div><br><br>
</body>
</html>
app.js
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
$scope.mySelections = [];
$scope.myData = [{empno: 111, name: "Moroni", id: 1},
{empno: 222, name: "Tiancum", id: 2},
{empno: 333, name: "Jacob", id: 3},
{empno: 444, name: "Nephi", id: 4},
{empno: 555, name: "Akon", id: 5},
{empno: 666, name: "Enos", id: 6}];
$scope.gridOptions = {
data: 'myData',
selectedItems: $scope.mySelections,
multiSelect: false
};
});
Q1: I want to hide the id column in ng-grid.
Q2: After hiding the id column, may I get the id value when I select some row?
How can modify the code?
Hear is the plunk: Plunk demo
You can set visible: false right in the column definition:
$scope.gridOptions = {
data: 'myData',
selectedItems: $scope.mySelections,
multiSelect: false,
columnDefs: [
{field: 'empno', displayName: 'empno', visible:false},
{field:'name', displayName:'name'}
]
};
You can also hide the column dynamically by adding this code after you define the grid;
var pos = $scope.gridOptions.columnDefs.map(function (e) { return e.field; }).indexOf('yourFieldName');
if ($scope.basicAdmin || $scope.superAdmin)
$scope.gridOptions.columnDefs[pos].visible = true;
else
$scope.gridOptions.columnDefs[pos].visible = false;
The angularjs grid array is $scope.gridOptions.columnDefs. Change the gridOptions to the name of your grid.
Replace "yourFieldName" with whatever field you are wanting to hide. Next, put whatever condition you want to test.
This took some time to figure out. Hopefully, it will save others some time.
Just add below lines to configuration and it will work
columnDefs: [
{field: 'empno', displayName: 'empno'},
{field:'name', displayName:'name'}
]
To hide particular column in AngularJS UI grid we can use visible: false property like as shown below.
columnDefs: [
{ field: 'userid', visible: false, displayName: 'UserId' },
{ field: 'username', displayName: 'UserName' },
{ field: 'branch', displayName: 'Education' }
]
If you want to check it in complete example you need to write the code like as shown below
<html ng-app="myApp">
<head>
<title>Hide Particular Column in Angularjs UI Grid with Example</title>
<link rel="stylesheet" type="text/css" href="http://angular-ui.github.com/ng-grid/css/ng-grid.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
<script type="text/javascript" src="http://angular-ui.github.com/ng-grid/lib/ng-grid.debug.js"></script>
<style type="text/css">
.gridStyle {
border: 1px solid rgb(212,212,212);
width: 400px;
height: 210px
}
</style>
</head>
<body ng-controller="MyCtrl">
<div class="gridStyle" ng-grid="gridOptions"></div>
<script type="text/javascript">
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function ($scope) {
$scope.mySelections = [];
$scope.myData = [{ userid: 1, username: "Anil Singh", branch:"B.tech" },
{ userid: 2, username: "Sunil", branch: "Msc" },
{ userid: 3, username: "Sushil", branch: "B.Tech" },
{ userid: 4, username: "Dilip", branch: "MBA" },
{ userid: 5, username: "Upendra", branch: "MD" },
{ userid: 6, username: "Reena", branch: "CA"}];
$scope.gridOptions = {
data: 'myData',
selectedItems: $scope.mySelections,
multiSelect: false,
columnDefs: [
{ field: 'userid', visible: false, displayName: 'UserId' },
{ field: 'username', displayName: 'UserName' },
{ field: 'branch', displayName: 'Education' } ]
};
});
</script>
</body>
</html>
We can use the following code after define the grid
if ($rootScope.CanDelete == false && $rootScope.CanEdit == false)
$scope.ConfigItemGrid.columnDefs[4].visible = false;
else
$scope.ConfigItemGrid.columnDefs[4].visible = true;
Use "hide: true" attribute as below in Angular 2, working fine for me:
columnDefs = [
{ headerName: "Make", hide: true, field: "make", editable: true, filter: 'text'},
{ headerName: "Model", field: "model", filter: 'text'},
{
headerName: "Price",
field: "price",
filter: 'number',
cellClass: 'rightJustify',
cellRenderer: function (params: any) {
return '$' + params.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); //thanks http://stackoverflow.com/users/28324/elias-zamaria
}
}
];
I suggest adding 'visible: false' to the column definitions. If you choose not to specify it in columnDefs, when you post the row back to whatever your backend is, you may null out that field. That's what I've experienced.

Sencha touch 2 - Display current location on map

I want to display my current location and get location coordinates to search nearby. Starting with the code below to display my location on the map, but its not working.
{
xtype: 'map',
useCurrentLocation: true
}
I defined a custom map class:
Ext.define("FindMyMoney.view.MapView", {
extend: "Ext.Map",
xtype: 'mapview',
config: {
useCurrentLocation: true,
listeners: {
maprender : function(comp, map){
new google.maps.Marker({
position: new google.maps.LatLng(this._geo.getLatitude(), this._geo.getLongitude()),
map: map
});
}
}
}
});
So it would render a marker on my current position. Simple.
Here is the code. It will show multiple markers with bounds:
Ext.define("iME.view.Maps", {
extend: "Ext.Map",
xtype: 'mapview',
config: {
mapOptions: {
center: new google.maps.LatLng(28.80010128657071, 77.28747820854187),
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false
},
listeners: {
maprender: function (comp, map) {
var markersll = [
['Noida', 28.80010128657071, 77.28747820854187],
['Coogee Beach', 24.80010128565, 73.2874782084457],
['Cronulla Beach', 25.80010128657071, 76.28747820854187],
['Manly Beach', 28.80010128657071, 72.28747820854187],
['Maroubra Beach', 9.052234, 75.243685]
];
var infowindow = new google.maps.InfoWindow();
var marker, i, pos;
var bounds = new google.maps.LatLngBounds();
for (i = 0; i < markersll.length; i++) {
pos = new google.maps.LatLng(markersll[i][1], markersll[i][2]);
bounds.extend(pos);
marker = new google.maps.Marker({
position: pos,
animation: google.maps.Animation.BOUNCE,
icon: 'http://thumb10.shutterstock.com/thumb_small/429058/131292377/stock-vector-map-super-marker-icon-131292377.jpg',
map: map,
title: 'Click Me ' + i
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent(markersll[i][0]);
infowindow.open(map, marker);
}
})(marker, i));
map.fitBounds(bounds);
}
}
}
}
});
I think this is a bug in ST2. I asked (I think you did :-) ) this question also in Sencha Forums: http://www.sencha.com/forum/showthread.php?156501-Maps-with-sencha-touch
My code in that forum did not worked. The code that I was using is as follows:
The index.html file looks like this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample app</title>
<script type="text/javascript" src="lib/touch/sencha-touch-all-debug.js"></script>
<link href="lib/touch/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript">
Ext.Loader.setConfig({
enabled : true,
path : {
PPL : 'ppl.js'
}
});
Ext.setup({
tabletStartupScreen : 'tablet_startup.png',
phoneStartupScreen : 'phone_startup.png',
icon : 'phone_startup.png',
onReady : function() {
Ext.create('PPL.App', {
fullscreen : true
});
}
})
</script>
</head>
<body>
</body>
</html>
And my ppl.js looks like this:
Ext.define('PPL.App', {
extend: 'Ext.Panel',
layout: 'vbox',
config: {
items: [{
xtype: 'toolbar',
title: 'Sample MAP'
}, {
xtype: 'panel',
layout: 'fit',
items: [{
xtype: 'map',
useCurrentLocation: true
}]
}]
}
});
If I change my ppl.js into the following:
Ext.define('PPL.App', {
extend: 'Ext.Map',
layout: 'fit',
config: {
items: [{
xtype: 'map',
useCurrentLocation: false
}]
}
});
Then it is working!
So, I think we need to wait untill next release, in the mean time learn ST2 :-)
Cheers!
Here is the code to show location current location
var geo = Ext.create('Ext.util.Geolocation', {
autoUpdate: false,
listeners: {
locationupdate: function(geo) {
var currentLat = geo.getLatitude();
var currentLng = geo.getLongitude();
var altitude = geo.getAltitude();
var speed = geo.getSpeed();
var heading= geo.getHeading();
},
locationerror: function(geo, bTimeout, bPermissionDenied, bLocationUnavailable, message) {
if(bTimeout)
Ext.Msg.alert('Timeout occurred',"Could not get current position");
else
alert('Error occurred.');
}
}
}
});
geo.updateLocation();

Resources