Return a value with highlighted color - sapui5

Requirement: I have a fragment.xml file which I am extending. The form element is currently being processed with a formatter.js where I am validating some values based on some condition:
In Fragment, the formatter function is getting called correctly
<Text text="{
parts: [
{path: 'ZName1'},
{path: 'ZStatus1'}
],
formatter: '.Formatter.delivery'
}" >
Formatter:
delivery: function(iName, iStatus) {
var sResult = "";
if(iStatus === "A" ) {
sResult = iName ;
} else if(iStatus === "P") {
sResult = iName ;
} else {
sResult = iName ;
}
return sResult ;
}
In the output, I should get sResult highlighted either in green, yellow, or red based on the condition.

Binding on text will not work for highlighting the text. refer the example for alternative solution.
<Text id="id" text="{ZName1}" class="{parts: [{path: 'ZName1'},{path: 'ZStatus1'} ],
formatter : '.Formatter.delivery'}">
In Format.js file:
delivery: function(iName, iStatus) {
var idText = this.byId("id");
if(iStatus === "A" ) {
idText.removeStyleClass("classForYellowColor");
idText.removeStyleClass("classForRedColor");
return "classForGreenColor";
} else if(iStatus === "P") {
idText.removeStyleClass("classForGreenColor");
idText.removeStyleClass("classForRedColor");
return "classForYellowColor";
} else {
idText.removeStyleClass("classForGreenColor");
idText.removeStyleClass("classForYellowColor");
return "classForRedColor";
}
}

Instead of plain sap.m.Text, take advantage of sap.m.ObjectStatus which works exactly like Text but supports semantic colors (via state) out-of-the-box.
Run the following snippet to see the results:
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/m/List",
"sap/m/CustomListItem",
"sap/m/ObjectStatus", // instead of Text
"sap/ui/core/ValueState",
"sap/ui/model/json/JSONModel",
], (List, Item, ObjectStatus, ValueState, JSONModel) => new List().bindItems({
path: "/myData",
template: new Item().addContent(new ObjectStatus({
text: "{ZName1}",
state: {
path: "ZStatus1",
formatter: status =>
status === "A" ? ValueState.Success : // Green
status === "P" ? ValueState.Warning : // Yellow
status === "Z" ? ValueState.Error : // Red
ValueState.None
},
}).addStyleClass("sapUiSmallMargin")),
}).setModel(new JSONModel({
myData: [
{
ZName1: "Success",
ZStatus1: "A"
},
{
ZName1: "Warning",
ZStatus1: "P"
},
{
ZName1: "Error",
ZStatus1: "Z"
},
{
ZName1: "None",
ZStatus1: ""
},
],
})).placeAt("content")));
<script>
window["sap-ui-config"] = {
libs: "sap.ui.core, sap.m",
preload: "async",
theme: "sap_belize",
compatVersion: "edge",
"xx-waitForTheme": true,
"xx-async": true
}
</script>
<script id="sap-ui-bootstrap" src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"></script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
We can see green, yellow, and red depending on the condition.

In Fragment file:
<Text text="{parts: [{path: 'ZName1'},{path: 'ZStatus1'}],
formatter : '.Formatter.delivery'}" >
In CSS file:
.greenTxtHlight {
color: green;
}
.yellowTxtHlight {
color: yellow;
}
.redTxtHlight {
color: red;
}
In Formatter file:
delivery: function(sName, sStatus) {
switch(sStatus){
case "A":
this.addStyleClass("greenTxtHlight");
this.removeStyleClass("yellowTxtHlight");
this.removeStyleClass("redTxtHlight");
break;
case "P":
this.removeStyleClass("greenTxtHlight");
this.addStyleClass("yellowTxtHlight");
this.removeStyleClass("redTxtHlight");
break;
case "G"://Update this
this.removeStyleClass("greenTxtHlight");
this.removeStyleClass("yellowTxtHlight");
this.addStyleClass("redTxtHlight");
break;
}
return sName;
}

Related

Formik - arrayfields -- validation handling

I am working with formik/material ui -- and yup validation. I am struggling to get validation showing/working on field arrays
my schema and validation looks like this currently for each field.
"fields": [
{
"type": "date",
"label": "Start Date",
"name": "startDate",
"validation": yup.date().default(function () { return new Date() }).required("date is required").nullable().typeError(''),
"minDate": moment().add(1, 'weeks'),
"maxDate": moment().add(8, 'weeks'),
"disablePast": true,
"disableFuture": false,
//"disabled": true
},
{
"type": "date",
"label": "End Date",
"name": "endDate",
"validation": yup.date().default(function () { return new Date() }).required("date is required").nullable().typeError(''),
"minDate": moment().add(1, 'weeks'),
"maxDate": moment().add(8, 'weeks'),
"disablePast": true,
"disableFuture": false,
//"disabled": true
}
]
I've seen on formik - they have some validation like this - but how do I apply it my code base for dates?
https://formik.org/docs/api/fieldarray
const schema = Yup.object().shape({
friends: Yup.array()
.of(
Yup.object().shape({
name: Yup.string().min(4, 'too short').required('Required'), // these constraints take precedence
salary: Yup.string().min(3, 'cmon').required('Required'), // these constraints take precedence
})
)
.required('Must have friends') // these constraints are shown if and only if inner constraints are satisfied
.min(3, 'Minimum of 3 friends'),
});
my fieldarray looks like this -- and I believe errors should appear under the field group -- the fields outer border goes red -- but it doesn't seem to work for when I null the date - like is required date working?
<>
<FieldArray
name={item.name}
onChange={event => {
console.log("event field array change", event)
}}
>
{({ insert, remove, push }) => (
<div className="field field-array">
<div className="row" key={0}>
{item.fields.map((ch, inx) => (
<span key={"x"+inx}>
<div className="col-x">
<Field
name={`${item.name}.${ch.name}`}
>
{({
field, // { name, value, onChange, onBlur }
form,
meta,
}) => (
<>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
label={ch.label}
disablePast={ch.disablePast}
disableFuture={ch.disableFuture}
minDate={moment(ch.minDate)}
maxDate={moment(ch.maxDate)}
value={field.value? moment(field.value).format('YYYY-MM-DD'): moment().format('YYYY-MM-DD')}
{...field}
onChange={(value) => {
form.setFieldValue(field.name, value);
this.props.onHandle(field.name, value);
}}
renderInput={(params) => {
return (<TextField {...params} name={field.name} />)
}}
/>
</LocalizationProvider>
{meta.touched && meta.error && (
<div className="error">{meta.error}</div>
)}
</>
)}
</Field>
</div>
{inx === 0 &&
(<span></span>)
}
</span>
))}
</div>
</div>
)}
</FieldArray>
</>
I worked this out
"validation": yup.array().of( yup.object().shape({ firstName: yup.string().min(4, 'too short').required('Required'), lastName: yup.string().min(3, 'cmon').required('Required'), }) ).min(1, 'Minimum of 1 friends')
-- but in the display of errors had to check if it was an array or a string to avoid a render error
under the add more button to display array errors of the main list.
<FormHelperText
error={(form.errors[parent.name] && form.errors[parent.name].length > 0 ? true : false)}
>
{typeof form.errors[parent.name] === "string" &&
<>{form.errors[parent.name]}</>
}
</FormHelperText>
and under the fields - meta errors
{(getHelperVisibility(values, ch)) &&
<FormHelperText
error={meta.touched && (meta.error && meta.error.length > 0 ? true : false)}
>
{meta.error}
</FormHelperText>
}

Set value in react-bootstrap-typeahead

First off, let me admit to being a React newbie...
I want to set the value displayed and selected in this component after the first render (eg from a button)
Here's my test code (with imports etc removed)
$(function () {
const data = [
{
DataID: 1,
DataType: 'Data1'
},
{
DataID: 2,
DataType: 'Data2'
},
{
DataID: 3,
DataType: 'Data3'
},
{
DataID: 4,
DataType: 'Data4'
}
]
ReactDOM.render(
<SelectionsExample
options={data}
preset={[data[1]]}
/>,
document.getElementById('divExampleSelector')
)
});
function SelectionsExample(props) {
const [options, setOptions] = useState(props.options);
const [preset, setPreset] = useState(props.preset);
function handleSelect(s) {
console.log((s ? s.DataType : 'Nothing') + ' selected');
}
function handlePreset() {
let s = options[2];
console.log('Preset', s);
setPreset([s]);
}
return (
<>
<Typeahead
id="selections-example"
options={options}
defaultSelected={preset ?? []}
onChange={(s) => handleSelect(s[0])}
labelKey="DataType"
clearButton
placeholder="Choose a value..."
/>
<Button
onClick={handlePreset}
variant="outline-secondary">Preset </Button>
</>
)
}
On first render, all works fine with as expected the second item in my options list shown.
But when I click the 'Preset' button, handlePreset runs but nothing changes in the control. I would have expected the selection to change to value of options[2].
If I change the Typeahead prop 'defaultSelected' to 'selected', then the only item I can select is the one I pass in in the 'preset' prop.
What am I doing wrong?
Using defaultSelected makes the typeahead uncontrolled, and will only display a preset selection when the component mounts. Since you want to be able to change the preset later, you should use selected to make the typeahead controlled:
function SelectionsExample(props) {
const [selected, setSelected] = useState(props.preset);
function handleSelect(s) {
console.log((s[0] ? s[0].DataType : 'Nothing') + ' selected');
setSelected(s);
}
function handlePreset() {
let s = props.options[2];
console.log('Preset', s);
setSelected([s]);
}
return (
<>
<Typeahead
clearButton
id="selections-example"
labelKey="DataType"
onChange={handleSelect}
options={props.options}
placeholder="Choose a value..."
selected={selected}
/>
<button onClick={handlePreset}>
Preset
</button>
</>
);
}
Working sandbox: https://codesandbox.io/s/heuristic-haze-3vugt

Angular PrimeNg Using autocomplete and passing REST object

I have an issue with PrimeNg autocomplete :
When i type any research, I obtain [Object Object] in the input text.
First I have an API call for getting a data set :
ngOnInit(): void {
this.getCategories();
}
private getCategories(): void {
const response = this.apiService.getCategories().subscribe(
(data) => {
this.categories = data as CategoriesModel[];
}
);
console.log('Get categories');
console.log('response ', response);
}
It allows me to retrive my data correctly (here is a sample) :
[
{
"id": "1",
"categoryName": "Name1",
"docDescription": "Description1 ..."
},
{
"id": "2",
"categoryName": "Name2",
"docDescription": "Description2"
}..
]
Now I try to handle my array of javascript objects in order to filter them :
I defined member variables in my component :
categories: CategoriesModel[];
filteredCategories: CategoriesModel[];
category: CategoriesModel;
I add this code into the HTML template:
<p-autoComplete
[(ngModel)]="category"
[suggestions]="filteredCategories"
(completeMethod)="filterCategories($event)"
[size]="30"
[minLength]="1" placeholder="Hint: type a letter"
[dropdown]="true">
<ng-template let-category pTemplate="item.categoryName">
<div class="ui-helper-clearfix" style="border-bottom:1px solid #D5D5D5">
{{category.id}}
<div style="font-size:18px;float:right;margin:10px 10px 0 0">{{category.categoryName}}</div>
</div>
</ng-template>
</p-autoComplete>
<span style="margin-left:50px">Category: {{category?.categoryName||'none'}}</span>
Now I try to use a filter method that will show in list results :
filterCategories(event): void {
this.filteredCategories = [];
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < this.categories.length; i++) {
this.category = this.categories[i];
if (this.category.categoryName.toLowerCase().indexOf(event.query.toLowerCase()) === 0) {
console.log(this.category.categoryName);
this.filteredCategories.push(this.category);
}
}
}
I finally solved this by modifying the template :
<p-autoComplete
[(ngModel)]="category"
[suggestions]="filteredCategories"
field = "categoryName"
(completeMethod)="filterCategories($event)"
[size]="30"
[minLength]="1" placeholder="Hint: type a letter"
[dropdown]="true">
<ng-template let-category pTemplate="categoryName">
<div class="ui-helper-clearfix" style="border-bottom:1px solid #D5D5D5">
{{category.id}} {{category.categoryName}}
</div>
</ng-template>
</p-autoComplete>

Vuetify datatable reload when performing CRUD operations

I have a simple vuetify datatable that performs CRUD operations. I am using axios and a mongo database. When I add a new item, the information is correctly displayed in the client side and posted in the mongo database. However, I cannot access to updated information of the mongo database, particularly the id that mongo created, unless I reload the webpage. I am newcomer in Vue, please be patient. A simplified version of the problem:
axios
.post('http://localhost:5000/dessert', {
name: this.editedItem.name
})
console.log(this.editedItem.name) // I CAN ACCES WITHOUT PROBLEM
console.log(this.editedItem._id) // I NEED TO RELOAD THE WEBPAGE IN ORDER TO ACCES THIS ELEMENT. THE ID THAT MONGO CREATED.
Vue file:
<template>
<v-data-table
:headers="headers"
:items="desserts"
sort-by="calories"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>My CRUD</v-toolbar-title>
<v-divider
class="mx-4"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on }">
<v-btn color="primary" dark class="mb-2" v-on="on">New Item</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
edit
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</template>
<script>
import axios from 'axios'
export default {
data: () => ({
dialog: false,
headers: [
{
text: 'Dessert (100g serving)',
value: 'name',
},
{ text: 'Calories', value: 'calories' },
{ text: 'Actions', value: 'action', sortable: false },
],
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
},
defaultItem: {
name: '',
calories: 0,
},
}),
mounted() {
this.fetchItems()
},
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
},
},
watch: {
dialog (val) {
val || this.close()
},
},
created () {
this.initialize()
},
methods: {
fetchItems(){
axios
.get('http://localhost:5000/dessert')
.then(response => (this.desserts = response.data.data))
},
editItem (item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.editedID = this.editedItem._id
this.name = this.editedItem.name
this.calories = this.editedItem.calories
this.dialog = true
},
deleteItem (item) {
const index = this.desserts.indexOf(item)
this.deletedItem = Object.assign({}, item)
console.log(this.deletedItem)
this.deletedID = this.deletedItem._id
console.log(this.deletedID)
if (confirm("Do you really want to delete?")) {
axios.delete(`http://localhost:5000/dessert/${this.deletedID}`);
this.desserts.splice(index, 1);
}
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () { // Edit Item
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem)
axios.delete(`http://localhost:5000/dessert/${this.editedItem._id}`)
axios
.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
})
// New Item
} else {
this.desserts.push(this.editedItem)
axios.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
})
}
this.close()
},
},
}
</script>
Python file:
from flask import Flask
from flask import jsonify
from flask import request
from flask_pymongo import PyMongo
from flask_cors import CORS
from bson.objectid import ObjectId
app = Flask(__name__)
#CORS(app)
# instantiate
app.config.from_object(__name__)
# enable CORS
CORS(app, resources={r'/*': {'origins': '*'}})
app.config['MONGO_DBNAME'] = 'restdb'
app.config['MONGO_URI'] = 'mongodb://localhost:27017/restdb'
mongo = PyMongo(app)
#app.route('/dessert', methods=['POST'])
def add_dessert():
dessert = mongo.db.desserts
name = request.json['name']
calories = request.json['calories']
dessert_id = dessert.insert({
'name': name,
'calories': calories
})
new_dessert = dessert.find_one({'_id': dessert_id })
output = {
'name' : new_dessert['name'],
'calories' : new_dessert['calories']
}
return jsonify({'result' : output})
#app.route('/dessert', methods=['GET'])
def get_all_desserts():
dessert = mongo.db.desserts
output = []
for s in dessert.find():
s['_id'] = str(s['_id'])
output.append({'_id' : s['_id'],
'name' : s['name'],
'calories' : s['calories']
})
return jsonify({'data' : output})
#app.route('/dessert/<dessert_id>', methods=['GET'])
def get_one_dessert(dessert_id):
dessert = mongo.db.desserts
s = dessert.find_one({"_id" : ObjectId(dessert_id)})
s['_id'] = str(s['_id'])
if s:
output = {'_id' : s['_id'], 'name' : s['name'], 'calories' : s['calories']}
else:
output = "No such name"
return jsonify({'result' : output})
#app.route('/dessert/<dessert_id>', methods=['DELETE'])
def delete_one_dessert(dessert_id):
dessert = mongo.db.desserts
s = dessert.find_one({"_id" : ObjectId(dessert_id)})
s['_id'] = str(s['_id'])
dessert.remove({"_id" : ObjectId(dessert_id)})
if s:
output = {'_id' : s['_id'], 'name' : s['name'], 'calories' : s['calories']}
else:
output = "No such name"
return jsonify({'result' : output})
if __name__ == '__main__':
app.run(debug=True)
If I understood it correctly, you want to be able to see in the front-end the newly added item, including the generated ID, after posting it to backend, right?
So, you can just simply call the fetchItems() once you finish posting the new item. It will automaticly update the array of the shown items, including the newly added ID.
The ID property is created when the item is added to the database, so it's not possible to have it unless the back-end gives it back to the front-end.
axios.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
}).then(response => {
this.fetchItems()
})
That means, once finishing the POST, fetchItems() again.

Dynamic number of tabs using ionic2-super-tabs tag

I do not have fixed number of tabs in my app page, so that using *ngFor in ionic2-super-tabs tag to get dynamic tabs as mentioned in below ts and html.
typescript :
import { AustraliaPage } from './australia';
import {CanadaPage} from './canada';
export class CountryPage {
Australia = AustraliaPage;
Canada= CanadaPage;
tabsLoaded = false;
ionViewDidLoad() {
this.testservice.getCoutrynames().subscribe(countries => {
this.tabs = countries;
// below is the output data of getCoutrynames() method
// "countries": [
// {
// "country_id": 1,
// "countryName": "Canada"
// },
// {
// "country_id": 2,
// "countryName": "Australia"
// }
// ]
this.tabsLoaded = true;
})
}
}
HTML :
<super-tabs scrollTabs="true" *ngIf="tabsLoaded">
<super-tab *ngFor="let tab of tabs" [root]="tab.countryName" title="tab.countryName"></super-tab>
</super-tabs>
But getting a below error.
Runtime Error
Uncaught (in promise): invalid link: Australia
Any help would be greatly appreciated.
You are setting the root to the string 'Australia' not the Australia object/page. Do something like this:
pages = {
Australia: AustraliaPage,
Canada: CanadaPage,
...
};
<super-tabs scrollTabs="true" *ngIf="tabsLoaded">
<super-tab *ngFor="let tab of tabs" [root]="pages[tab.countryName]" title="tab.countryName"></super-tab>
</super-tabs>