I'm new to Angular, I have a table that I need to display with two languages based on the user's choice, I have searched a lot for this however, couldn't find anything useful for my case, I have made a work around that doesn't really fulfill my needs as when I go to another page and then back to this page it displays the data with the wrong choice as the variable inside the localstorage returns with undefined. Please advise if you have a better approach as I totally understand that mine is really bad. Thanks in advance.
employee.component.ts
ngOnInit() {
this.switchLanguage = localStorage.getItem('selectedLanguage');
this.lang = localStorage.getItem('lang')
this.getActiveEmployees();
}
getAllEmployees(){
if(this.switchLanguage === "en" || this.browserLang === "en"){
console.log(this.switchLanguage + " Da5al hena lee 1")
this.settings = {
totalKey: 'total',
pager: {
display: true,
perPage: '5'
},
mode: 'external',
actions: false,
columns: {
firstName_FL: {
title: 'First Name',
data: this.employees.firstName_FL,
},
secondName_FL: {
title: 'Second Name',
data: this.employees.secondName_FL,
},
lastName_FL: {
title: 'Last Name',
data: this.employees.lastName_FL,
},
hiringDate: {
title: 'Hiring Date',
data: this.employees.hiringDate,
type: 'date',
filter: {
type: 'daterange'
},
valuePrepareFunction: (lastLoginTime: any) => {
return lastLoginTime.slice(0, 10)
}
},
firstContractingSalary: {
title: 'First Contracting Salary',
valuePrepareFunction: (firstContractingSalary) => {
this.formatedOutputValue = this.currenctPipe.transform(firstContractingSalary, 'EGP ',
'symbol', '1.2-2');
return this.formatedOutputValue
}
},
position: {
title: 'Position',
valuePrepareFunction: (position) => {
return position.name_FL;
},
sort: true
},
department: {
title: 'Department',
valuePrepareFunction: (department) => {
return department.name_FL;
}
},
employeeJobStatuses: {
title: 'Status',
filter: false,
sort: false,
valuePrepareFunction: (employeeJobStatuses) => {
this.statusValidation = employeeJobStatuses.map((element, i) => [
element.status
])
for(var i = 0; i < this.statusValidation.length; i++){
if(this.statusValidation[i][i] === "STATUS_ACTIVE"){
return "Active"
} else if(this.statusValidation[i][i] === "STATUS_SUSPENDED"){
return "Suspended"
} else if(this.statusValidation[i][i] === "STATUS_TERMINATED"){
return "Terminated"
} else if(this.statusValidation[i][i] === "STATUS_INACTIVE"){
return "Inactive"
}
}
}
}
}
};
} else if(this.switchLanguage === "ar" || this.browserLang === "ar"){
this.settings = {
totalKey: 'total',
pager: {
display: true,
perPage: '5'
},
mode: 'external',
actions: false,
columns: {
firstName_SL: {
title: 'الاسم الاول',
data: this.employees.firstName_SL,
},
secondName_SL: {
title: 'الاسم الاوسط',
data: this.employees.secondName_SL,
},
lastName_SL: {
title: 'الاسم الاخير',
data: this.employees.lastName_SL,
},
hiringDate: {
title: 'تاريخ التوظيف',
data: this.employees.hiringDate,
type: 'date',
filter: {
type: 'daterange'
},
valuePrepareFunction: (lastLoginTime: any) => {
return lastLoginTime.slice(0, 10)
}
},
firstContractingSalary: {
title: 'قيمة التعاقد المبدأية',
valuePrepareFunction: (firstContractingSalary) => {
this.formatedOutputValue = this.currenctPipe.transform(firstContractingSalary, 'EGP ', 'symbol', '1.2-2');
return this.formatedOutputValue
}
},
position: {
title: 'المركز',
valuePrepareFunction: (position) => {
return position.name_SL;
},
sort: true
},
department: {
title: 'القسم',
valuePrepareFunction: (department) => {
return department.name_SL;
}
},
employeeJobStatuses: {
title: 'الحالة',
filter: false,
sort: false,
valuePrepareFunction: (employeeJobStatuses) => {
this.statusValidation = employeeJobStatuses.map((element, i) => [
element.status
])
for(var i = 0; i < this.statusValidation.length; i++){
if(this.statusValidation[i][i] === "STATUS_ACTIVE"){
return "نشيط"
} else if(this.statusValidation[i][i] === "STATUS_SUSPENDED"){
return "معلق"
} else if(this.statusValidation[i][i] === "STATUS_TERMINATED"){
return "منتهي"
} else if(this.statusValidation[i][i] === "STATUS_INACTIVE"){
return "غير نشط"
}
}
}
}
}
};
}
this.translate.onLangChange.subscribe(language => {
this.browserLang = language.lang;
if(this.browserLang === "en"){
this.settings = {
totalKey: 'total',
pager: {
display: true,
perPage: '5'
},
mode: 'external',
actions: false,
columns: {
firstName_FL: {
title: 'First Name',
data: this.employees.firstName_FL,
},
secondName_FL: {
title: 'Second Name',
data: this.employees.secondName_FL,
},
lastName_FL: {
title: 'Last Name',
data: this.employees.lastName_FL,
},
hiringDate: {
title: 'Hiring Date',
data: this.employees.hiringDate,
type: 'date',
filter: {
type: 'daterange'
},
valuePrepareFunction: (lastLoginTime: any) => {
return lastLoginTime.slice(0, 10)
}
},
firstContractingSalary: {
title: 'First Contracting Salary',
valuePrepareFunction: (firstContractingSalary) => {
this.formatedOutputValue = this.currenctPipe.transform(firstContractingSalary, 'EGP ', 'symbol', '1.2-2');
return this.formatedOutputValue
}
},
position: {
title: 'Position',
valuePrepareFunction: (position) => {
return position.name_FL;
},
sort: true
},
department: {
title: 'Department',
valuePrepareFunction: (department) => {
return department.name_FL;
}
},
employeeJobStatuses: {
title: 'Status',
filter: false,
sort: false,
valuePrepareFunction: (employeeJobStatuses) => {
this.statusValidation = employeeJobStatuses.map((element, i) => [
element.status
])
for(var i = 0; i < this.statusValidation.length; i++){
if(this.statusValidation[i][i] === "STATUS_ACTIVE"){
return "Active"
} else if(this.statusValidation[i][i] === "STATUS_SUSPENDED"){
return "Suspended"
} else if(this.statusValidation[i][i] === "STATUS_TERMINATED"){
return "Terminated"
} else if(this.statusValidation[i][i] === "STATUS_INACTIVE"){
return "Inactive"
}
}
}
}
}
};
} else if(this.browserLang === "ar"){
this.settings = {
totalKey: 'total',
pager: {
display: true,
perPage: '5'
},
mode: 'external',
actions: false,
columns: {
firstName_SL: {
title: 'الاسم الاول',
data: this.employees.firstName_SL,
},
secondName_FL: {
title: 'الاسم الاوسط',
data: this.employees.secondName_SL,
},
lastName_FL: {
title: 'الاسم الاخير',
data: this.employees.lastName_SL,
},
hiringDate: {
title: 'تاريخ التوظيف',
data: this.employees.hiringDate,
type: 'date',
filter: {
type: 'daterange'
},
valuePrepareFunction: (lastLoginTime: any) => {
return lastLoginTime.slice(0, 10)
}
},
firstContractingSalary: {
title: 'قيمة التعاقد المبدأية',
valuePrepareFunction: (firstContractingSalary) => {
this.formatedOutputValue = this.currenctPipe.transform(firstContractingSalary, 'EGP ', 'symbol', '1.2-2');
return this.formatedOutputValue
}
},
position: {
title: 'المركز',
valuePrepareFunction: (position) => {
return position.name_SL;
},
sort: true
},
department: {
title: 'القسم',
valuePrepareFunction: (department) => {
return department.name_SL;
}
},
employeeJobStatuses: {
title: 'الحالة',
filter: false,
sort: false,
valuePrepareFunction: (employeeJobStatuses) => {
this.statusValidation = employeeJobStatuses.map((element, i) => [
element.status
])
for(var i = 0; i < this.statusValidation.length; i++){
if(this.statusValidation[i][i] === "STATUS_ACTIVE"){
return "نشيط"
} else if(this.statusValidation[i][i] === "STATUS_SUSPENDED"){
return "معلق"
} else if(this.statusValidation[i][i] === "STATUS_TERMINATED"){
return "منتهي"
} else if(this.statusValidation[i][i] === "STATUS_INACTIVE"){
return "غير نشط"
}
}
}
}
}
};
}
})
this.loading = true;
this.restService.GetAllEmployees().subscribe((res: any) => {
this.loading = false;
this.employees = res.data.employees;
const maw = JSON.stringify(this.employees)
this.jsonObj = JSON.parse(maw);
})
}
dont forget to inject TranslateService in ur constrctor ,and add id , name resources in ar.josn , and en.json ,try this
columnheaders:any;
ngOnInit(){
this.columnheaders = []
this.translate.get('the id').subscribe(label =>this.columnheaders[0]= label);
this.translate.get('the name').subscribe(label =>this.columnheaders[1]= label);
}
this.settings = {
columns: {
name: {
title:this.columnheaders[0],
type: 'string',
}
name: {
title:this.columnheaders[1],
type: 'string',
}
}}
Related
When I try to draw a graph, I get an error:echarts.min.js:45 Uncaught TypeError: Bind must be called on a function at bind (<anonymous>) at Bd (echarts.min.js:45:130031)
My echarts-init.js:
let domTemp = document.getElementById("main");
let mytempChart = echarts.init(domTemp, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
runDaysDatas(sens_data_result, sens_name_list);
function runDaysDatas(sens_data_result, sens_name_list) {
const sens_names = sens_name_list;
const datasetWithFilters = [];
const seriesList = [];
echarts.util.each(sens_names, function (sens) {
var datasetId = 'dataset_' + sens;
datasetWithFilters.push({
id: datasetId,
fromDatasetId: sens_data_result,
transform: {
type: 'filter',
config: {
and: [
{ dimension: 'Uid', '=': sens }
]
}
}
});
seriesList.push({
type: 'line',
datasetId: datasetId,
showSymbol: false,
name: sens,
endLabel: {
show: true,
formatter: function (params) {
return params.value[3] + ': ' + params.value[0];
}
},
labelLayout: {
moveOverlap: 'shiftY'
},
emphasis: {
focus: 'series'
},
encode: {
x: 'Date',
y: 'Temperature',
label: ['Name', 'Temperature'],
itemName: 'Date',
tooltip: ['Temperature']
}
});
});
option = {
animationDuration: 10000,
dataset: [
{
id: 'dataset_sens_names',
source: sens_data_result
},
...datasetWithFilters
],
title: {
text: 'Temperature for Day'
},
tooltip: {
order: 'valueDesc',
trigger: 'axis'
},
xAxis: {
type: 'category',
nameLocation: 'middle'
},
yAxis: {
name: 'Temperature'
},
grid: {
right: 140
},
series: seriesList
};
mytempChart.setOption(option);
}
In sens_data_result i pass data from api.
In sens_name_list i pass names of the sensors.
The console does not send errors to my script, it swears at the library. I took an example from the official site and remade it for my task, displaying the temperature by time of day with the name of the sensor. There can be N number of graphs on one chart.
Thnx for help!
Okey, i'm a solved the problem, this is decision:
let url = '/api/sensdata';
let domTemp = document.getElementById("main");
let mytempChart = echarts.init(domTemp, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
$.get(
url,
sensors_uid,
(_rawData) => {
runDaysDatas(_rawData, sens_name_list);
}
);
function runDaysDatas(_rawData, sens_names) {
const datasetWithFilters = [];
const seriesList = [];
_rawData.unshift(['Name', 'Date', 'Humidity', 'Temperature']);
echarts.util.each(sens_names, function (sens) {
var datasetId = 'dataset_' + sens;
datasetWithFilters.push({
id: datasetId,
fromDatasetId: 'dataset_raw',
transform: {
type: 'filter',
config: {
and: [
{ dimension: 'Name', '=': sens }
]
}
}
});
seriesList.push({
type: 'line',
datasetId: datasetId,
showSymbol: false,
name: sens,
endLabel: {
show: true,
formatter: function (params) {
return 'Uid ' + params.value[0] + ': ' + params.value[3] + '°C';
}
},
labelLayout: {
moveOverlap: 'shiftY'
},
emphasis: {
focus: 'series'
},
encode: {
x: 'Date',
y: 'Temperature',
label: ['Name', 'Temperature'],
itemName: 'Temperature',
tooltip: ['Temperature']
},
});
});
console.log(seriesList);
option = {
toolbox: {
show : true,
feature : {
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
legend: {},
dataset: [
{
id: 'dataset_raw',
source: _rawData
},
...datasetWithFilters
],
tooltip: {
order: 'valueDesc',
trigger: 'axis'
},
xAxis: {
type: 'time',
nameLocation: 'middle',
axisLabel: {
formatter: (function(value){
moment.locales('RU_ru');
return moment(value).format('MM/DD HH:mm');
})
}
},
yAxis: [
{
type : 'value',
axisLabel : {
formatter: '{value} °C'
}
}
],
grid: {
right: 140
},
series: seriesList
};
mytempChart.clear();
mytempChart.setOption(option);
}
window.addEventListener('resize', mytempChart.resize);
I have an existing table wherein I used a library called react-bootstrap-table-next
It serves its purpose of showing data in a table in which the values are from a JSON response
However, I want to add an Action column containing edit and delete
I want to achieve this using material-ui icons
Any advice as to how should I start? Should I fully convert my table first into material-ui to achieve this?
OR I can just edit profiles state array and map it into a new array containing icons?
ProfileMaintenance.js
const [profiles, setProfiles] = useState([]); // populate table with saved profiles
const retrieveProfiles = useCallback(() => {
ProfileMaintenanceService.retrieveProfiles()
.then((response) => {
console.log(
"ProfileMaintenance - retrieveProfiles response.data >>> ",
response.data
);
setProfiles(response.data);
})
.catch((error) => {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers); // send to logger
if (
error.response.data.error !== undefined &&
error.response.data.error != ""
) {
store.addNotification({
...notification,
type: "danger",
message: error.response.data.error,
dismiss: {
duration: 5000,
},
});
} else {
store.addNotification({
...notification,
type: "danger",
message:
"Server responded with a status code that falls out of the range of 2xx",
dismiss: {
duration: 5000,
},
});
}
} else if (error.request) {
// if API is down
console.log(error.request); // send to logger
store.addNotification({
...notification,
type: "danger",
message: "Request was made but no response was received",
dismiss: {
duration: 5000,
},
});
}
});
});
const columnsProfile = [
// {
// headerStyle: {
// backgroundColor: '#b3b3b3'
// },
// dataField: 'id', // for dev only
// text: 'ID',
// sort: true
// },
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "profileName",
text: "Name",
sort: true,
filter: textFilter(),
},
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "createdBy",
text: "Creator",
sort: true,
},
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "creationDate",
text: "Creation Date",
sort: true,
// filter: dateFilter()
},
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "lastModifier",
text: "Last Modifier",
sort: true,
},
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "lastModification",
text: "Last Modification",
sort: true,
},
{
headerStyle: {
backgroundColor: "#b3b3b3",
},
dataField: "action",
text: "Action",
},
];
const options = {
paginationSize: 4,
pageStartIndex: 1,
alwaysShowAllBtns: true,
hideSizePerPage: true,
firstPageText: "First",
prePageText: "Back",
nextPageText: "Next",
lastPageText: "Last",
nextPageTitle: "First page",
prePageTitle: "Pre page",
firstPageTitle: "Next page",
lastPageTitle: "Last page",
showTotal: true,
paginationTotalRenderer: customTotal,
sizePerPageList: [
{
text: "5",
value: 5,
},
{
text: "10",
value: 10,
},
{
text: "All",
value: profiles.length,
},
],
};
return (
<BootstrapTable
keyField="id"
hover
data={profiles}
columns={columnsProfile}
defaultSorted={defaultSorted}
filter={filterFactory()}
selectRow={selectRowClient}
noDataIndication="No record(s) found."
pagination={paginationFactory(options)}
/>
)
As you want material icon, I suggest to use material ui table. Please below example to edit or delete row from material ui table.
import React from 'react';
import MaterialTable from 'material-table';
export default function MaterialTableDemo() {
const [state, setState] = React.useState({
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Surname', field: 'surname' },
{ title: 'Birth Year', field: 'birthYear', type: 'numeric' },
{
title: 'Birth Place',
field: 'birthCity',
lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' },
},
],
data: [
{ name: 'Mehmet', surname: 'Baran', birthYear: 1987, birthCity: 63 },
{
name: 'Zerya Betül',
surname: 'Baran',
birthYear: 2017,
birthCity: 34,
},
],
});
return (
<MaterialTable
title="Editable Example"
columns={state.columns}
data={state.data}
editable={{
onRowAdd: (newData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.push(newData);
return { ...prevState, data };
});
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
if (oldData) {
setState((prevState) => {
const data = [...prevState.data];
data[data.indexOf(oldData)] = newData;
return { ...prevState, data };
});
}
}, 600);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.splice(data.indexOf(oldData), 1);
return { ...prevState, data };
});
}, 600);
}),
}}
/>
);
}
I am working the react-data-grid library to create an filterable datatable in react. All of their examples use the depreciated React.createClass method, and I am trying to refactor to the ES6 Class Components.
Specifically, I am trying to refactor the Filterable Grid example:
demo
source
gist of non-refactored adaption that is working
My refactored code looks like this:
import React from 'react'
import ReactDataGrid from 'react-data-grid'
const { Toolbar, Data: { Selectors } } = require('react-data-grid-addons')
class FilterableTable extends React.Component {
constructor(props) {
super(props);
this._columns = [
{
key: 'id',
name: 'ID',
width: 80
},
{
key: 'task',
name: 'Title',
editable: true
},
{
key: 'priority',
name: 'Priority',
editable: true
},
{
key: 'issueType',
name: 'Issue Type',
editable: true
},
{
key: 'complete',
name: '% Complete',
editable: true
},
{
key: 'startDate',
name: 'Start Date',
editable: true
},
{
key: 'completeDate',
name: 'Expected Complete',
editable: true
}
];
this.state = { rows: this.createRows(1001), filters: {} };
console.log(this.state);
}
getRandomDate = (start, end) => {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toLocaleDateString();
}
createRows = () => {
let rows = [];
for (let i = 1; i < 1000; i++) {
rows.push({
id: i,
task: 'Task ' + i,
complete: Math.min(100, Math.round(Math.random() * 110)),
priority: ['Critical', 'High', 'Medium', 'Low'][Math.floor((Math.random() * 3) + 1)],
issueType: ['Bug', 'Improvement', 'Epic', 'Story'][Math.floor((Math.random() * 3) + 1)],
startDate: this.getRandomDate(new Date(2015, 3, 1), new Date()),
completeDate: this.getRandomDate(new Date(), new Date(2016, 0, 1))
});
}
return rows;
}
getRows = () => {
return Selectors.getRows(this.state);
}
getSize = () => {
return this.getRows().length;
}
rowGetter = ( rowIdx ) => {
let rows = this.getRows();
return rows[rowIdx];
}
handleFilterChange = ({ filter }) => {
let newFilters = Object.assign({}, this.state.filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
this.setState({ filters: newFilters });
}
onClearFilters = () => {
// all filters removed
this.setState({filters: {} });
}
render() {
return (
<ReactDataGrid
columns={this._columns}
rowGetter={this.rowGetter}
enableCellSelect={true}
rowsCount={this.getSize()}
minHeight={800}
toolbar={<Toolbar enableFilter={true}/>}
onAddFilter={this.handleFilterChange}
onClearFilters={this.onClearFilters} />);
}
}
export default FilterableTable
Issue:
An issue arises when I click the filter button - a new header row is rendered (via the Toolbar component), but there is no input field. This screenshot shows the two examples side by side - my ES6 version on top and the createClass version on the bottom:
I am not sure what is causing this, but have a feeling it might be due to the way I am importing Toolbar ? Any help or a point in the right direction would be greatly appreciated ! (As well as any other suggestions re refactoring this component.)
To enable filtering for a given column, you need to set filterable=true for that column. So, add filterable:true to each object in this._columns. For more info, check http://adazzle.github.io/react-data-grid/examples.html#/filterable-grid.
this._columns = [
{
key: 'id',
name: 'ID',
width: 80
},
{
key: 'task',
name: 'Title',
editable: true,
filterable:true
},
{
key: 'priority',
name: 'Priority',
editable: true,
filterable:true
},
{
key: 'issueType',
name: 'Issue Type',
editable: true,
filterable:true
},
{
key: 'complete',
name: '% Complete',
editable: true,
filterable:true
},
{
key: 'startDate',
name: 'Start Date',
editable: true,
filterable:true
},
{
key: 'completeDate',
name: 'Expected Complete',
editable: true,
filterable:true
}
];
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>
I'm trying to add a Category to my Post, so I have a form with a select box where the user can choose the category to associate with his post. The problem is that when I submit my form, I had an error because the field Category is not an object... I tried to hook the form with formToDoc to modify the doc and make the category element look good but it doesn't work. I'm guessing that I have to deal with the validation of my data but I don't know how ?
Here are my collections CPDM and Category :
Category = new Mongo.Collection("category");
// Création du schéma des catégories
Category.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Catégorie",
max: 200
},
value: {
type: String,
label: "Catégorie Value",
max: 200
}
}));
// Création du schéma des CPDM
CPDM.attachSchema(new SimpleSchema({
title: {
type: String,
label: "Titre",
max: 200
},
content: {
type: String,
label: "Contenu",
autoform: {
afFieldInput: {
type: "textarea",
rows: 15
}
}
},
createdAt: {
type: Date,
autoform: {
omit: true
},
autoValue: function() {
if (this.isInsert) {
return new Date;
}
else {
this.unset();
}
}
},
author: {
type: String,
autoform: {
omit: true
},
autoValue: function () {
if (this.isInsert) {
if (Meteor.user()) {
return Meteor.user().username;
} else {
return "Anonyme";
}
} else {
this.unset();
}
}
},
ranking: {
type: Number,
autoValue: function () {
if (this.isInsert) {
return 0;
}
},
autoform: {
omit: true
},
min: 0,
label: "Note"
},
voters: {
type: [String],
autoform: {
omit: true
},
autoValue: function () {
if (this.isInsert) {
return [];
}
}
},
selected: {
type: Boolean,
autoform: {
omit: true
},
autoValue: function () {
if (this.isInsert) {
return false;
}
}
},
category: {
type: Object,
optional: true,
label: "Catégorie",
autoform: {
firstOption: "Sélectionner une catégorie"
}
}
}));
And here is my formHook :
AutoForm.hooks({
createCPDM: { // ID du formulaire
formToDoc: function(doc) {
var categoryName = $("[name='category'] option:selected").text();
doc.category = {name:categoryName, value:doc.category};
return doc;
},
onSubmit: function (doc) { // Gestion du formulaire de soumission
var error = null;
var title = doc.title;
var content = doc.content;
var category = doc.category;
var captcha = $("#captcha").val();
var formData = {
title: title,
content: content,
category: category
};
if (captcha == 4) {
Meteor.call('createCPDM', formData, function (err) {
if (err) {
error = new Error("Une erreur s'est produite");
}
});
}
else {
error = new Error("Mauvais captcha");
}
if (error === null) {
this.done(); // Appelle onSuccess
}
else {
this.done(error); // Appelle onError
}
return false;
},
Thank's for helping !
I found the solution, I had to adjust my Post collection to make category to be an object. Like this :
category: {
type: Object,
optional: true,
label: "Catégorie",
autoform: {
firstOption: "Sélectionner une catégorie"
}
},
"category.name": {
type: String,
label: "Catégorie",
max: 200,
autoform: {
omit: true
}
},
"category.value": {
type: String,
label: "Catégorie Value",
max: 200,
autoform: {
omit: true
}
}