Getting required error even when the value is present. This value is set programmatically(as I am using v-model in the parent component). When I change the input value manually error message disappears
From Vue dev tools: Not sure why the modelValue is undefined and if it might be the reason
Child component
< script >
import {
requiredRule
} from "#/composables/validationRules";
import useFieldEntry from "#/composables/fieldEntry";
export default {
name: "NumberField",
props: {
id: {},
rules: {
default: []
},
readOnly: {
default: false
},
modelValue: {
default: ""
},
isRequired: {
default: false
},
decimalPlaces: {
default: null
},
},
setup(props, context) {
const {
isDecimalNumber,
isNumber
} = useFieldEntry();
let isInteger = !props.decimalPlaces ||
props.decimalPlaces == null ||
props.decimalPlaces == 0;
const updateValue = (event) => {
context.emit("update:modelValue", event.target.value);
};
//this is needed if user enters a decimal at end and no digits after
const convertToString = (event) => {
context.emit("update:modelValue", getFormattedValue(event.target.value));
};
function getFormattedValue(value) {
if (!isInteger) {
//0 is a valid value but js considers 0 as false and hence adding the or condition
return parseFloat(value) || parseFloat(value) === 0 ?
parseFloat(value).toFixed(props.decimalPlaces) :
null;
} else {
return parseFloat(value) || parseFloat(value) === 0 ?
parseFloat(value).toFixed(0) :
null;
}
}
let validationRules = [];
if (props.rules && props.rules.length > 0) {
validationRules = validationRules.concat(props.rules);
}
if (props.isRequired) {
validationRules.push(requiredRule);
}
function handler(e) {
return isInteger ? isNumber(e) : isDecimalNumber(e);
}
return {
validationRules,
isDecimalNumber,
convertToString,
isNumber,
handler,
updateValue,
};
},
}; <
/script>
<template>
<v-text-field
:id="id"
label=""
density="compact"
:variant="readOnly ? 'plain' : 'filled'"
:readOnly="readOnly"
:value="modelValue"
#input="updateValue"
hide-details="auto"
:required="isRequired"
#keypress="handler"
#blur="convertToString"
:decimalPlaces="decimalPlaces"
:rules="validationRules"
/>
</template>
validationRules.js:
export const requiredRule = (value) => !!value || "Required";
Parent component:
<script>
export default {
name: "InspectionElements",
setup() {
const modifyElementForm = ref(null);
let selectedElement={
"QUANTITY":"6",
}
function validate() {
return modifyElementForm.value.validate();
}
return {selectedElement, validate}
}
</script>
Parent:
<v-form ref="modifyElementForm">
<NumberField
id="text_qty"
:readOnly="false"
v-model="selectedElement.QUANTITY"
:showSpinner="true"
:isRequired="true"/>
<v-btn
#click="
{validate();
}
"
>
<span>Add</span>
</v-btn></v-form>
Related
methods: {
onShowModal(idWinnerPrize, idPrize, curUser) {
this.userEdit = _.cloneDeep(curUser);
if (idWinnerPrize === idPrize) {
this.confirmAward = false;
this.isDelete = true;
this.isFull = false;
this.userEdit.prize = null;
} else {
this.confirmAward = true;
this.isDelete = false;
let countPrize = this.winners.reduce((acc, cur) => {
if (cur.prize_id === idPrize) {
acc += 1;
}
return acc;
}, 0);
const currentPrize = this.prizes.find(
item => item.id === idPrize
);
if (currentPrize && currentPrize.quantity <= countPrize) {
this.isFull = true;
} else {
this.userEdit.prize = idPrize;
this.isFull = false;
}
}
this.showModal = true;
},
onCloseModal() {
this.showModal = false;
this.notify = false;
},
this is my function show and close modal, and use axios.post to update
async onEdit() {
let res = null;
let userIndex = this.winners.findIndex(
item => item.id == this.userEdit.id
);
res = await axios.post( this.isFull ? REMOVE_PRIZE : EDIT_PRIZE, { id: this.userEdit.id, prize_id: this.userEdit.prize } ).then((res) => { return res.data }).catch((error => {
return error
}));
res = true;
if (userIndex > -1 && res) {
this.winners[userIndex] = this.userEdit;
}
this.showModal = false;
},
updated() {
this.prizes = this.contest.prizes;
this.winners = this.posts.data;
}
},
props: ['contest', 'actionUrl', 'method', 'posts'],
data() {
return {
notify: true,
showModal: false,
confirmAward: true,
isDelete: false,
isFull: false,
userEdit: {},
prizes: [],
winners: []
};
},
mounted() {
this.showModal = true;
}
};
that my Vue component, I want to update the page without refresh the page after I click on edit, I don't know why Axios post can't update data. If I update, I need to refresh the page to see my updated data
There isn't any documentation for how the array meta info (arrayLength and sliceStart) should be implemented using facebook's graphql-relay-js helper library.
https://github.com/graphql/graphql-relay-js/issues/199
I managed to get it to work using the following implemention however I am guessing there is an easier/more correct way to do this.
Retrieve rows and row count from database
function transformRole(role: Role) {
return { ...role, roleId: role.id };
}
async function getRolesSlice({ roleId, after, first, last, before }: any): Promise<[Role[], number]> {
const queryBuilder = repository.createQueryBuilder();
if (roleId !== undefined) {
queryBuilder.where('id = :roleId', { roleId });
}
if (before) {
const beforeId = cursorToOffset(before);
queryBuilder.where('id < :id', { id: beforeId });
}
if (after) {
const afterId = cursorToOffset(after);
queryBuilder.where({
id: MoreThan(Number(afterId))
});
}
if (first === undefined && last === undefined) {
queryBuilder.orderBy('id', 'ASC');
}
if (first) {
queryBuilder.orderBy('id', 'ASC').limit(first);
}
if (last) {
queryBuilder.orderBy('id', 'DESC').limit(last);
}
return Promise.all([
queryBuilder.getMany()
.then(roles => roles.map(transformRole)),
repository.count() // Total number of roles
]);
}
Roles resolver
resolve: (_, args) =>
getRolesSlice(args)
.then(([results, count]) => {
const firstId = results[0] && results[0].roleId;
let sliceStart = 0;
if (args.first) {
sliceStart = firstId;
}
if (args.last) {
sliceStart = Math.max(firstId - args.last, 0);
}
if (args.after && args.last) {
sliceStart += 1;
}
return connectionFromArraySlice(
results,
args,
{
arrayLength: count + 1,
sliceStart
}
);
})
},
Edit:
This is what I came up with which is a little cleaner and seems to be working correctly.
const initialize = () => {
repository = getConnection().getRepository(Role);
}
function transformRole(role: Role) {
return { ...role, roleId: role.id };
}
function getRolesSlice(args: any):
Promise<[
Role[],
any,
{ arrayLength: number; sliceStart: number; }
]> {
if (!repository) initialize();
const { roleId, after, first, last, before } = args;
const queryBuilder = repository.createQueryBuilder();
if (roleId !== undefined) {
queryBuilder.where('id = :roleId', { roleId });
}
if (before !== undefined) {
const beforeId = cursorToOffset(before);
queryBuilder.where({
id: LessThan(beforeId)
});
}
if (after !== undefined) {
const afterId = cursorToOffset(after);
queryBuilder.where({
id: MoreThan(Number(afterId))
});
}
if (first !== undefined) {
queryBuilder.orderBy('id', 'ASC').limit(first);
} else if (last !== undefined) {
queryBuilder.orderBy('id', 'DESC').limit(last);
} else {
queryBuilder.orderBy('id', 'ASC');
}
return Promise.all([
queryBuilder.getMany()
.then(roles => roles.map(transformRole))
.then(roles => last !== undefined ? roles.slice().reverse() : roles),
repository.count()
]).then(([roles, totalCount]) =>
[
roles,
args,
{
arrayLength: totalCount + 1,
sliceStart: roles[0] && roles[0].roleId
}
]
);
}
// Resolver
roles: {
type: rolesConnection,
args: {
...connectionArgs,
roleId: {
type: GraphQLString
}
},
resolve: (_, args) =>
getRolesSlice(args)
.then((slice) => connectionFromArraySlice(...slice))
},
I'm trying to achieve infinite scrolling using ag grid react component, but it doesn't seems to be working.
here is my implementation :
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid/dist/styles/ag-grid.css';
import 'ag-grid/dist/styles/ag-theme-balham.css';
class TasksGridContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
gridOptions: {
//virtual row model
rowModelType: 'infinite',
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2,
components: {
loadingRenderer: function(params) {
console.log('loadingCellRenderer', params);
if (params.value !== undefined) {
return params.value;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
defaultColDef: {
editable: false,
enableRowGroup: true,
enablePivot: true,
enableValue: true
}
}
};
}
componentDidMount() {
this.props.actions.getAssignedTasks();
this.props.actions.getTeamTasks();
}
componentWillReceiveProps(newProps) {
if (this.props.taskView.taskGrid.listOfTasks.length > 0) {
this.setState({
loading: false ,
gridOptions: {
datasource: this.props.taskView.taskGrid.listOfTasks
}
});
}
}
render() {
return (
<div id="tasks-grid-container">
<div style={Style.agGrid} id="myGrid" className="ag-theme-balham">
<AgGridReact
columnDefs={this.props.taskView.taskGrid.myTaskColumns}
rowData={this.props.taskView.taskGrid.listOfTasks}
gridOptions={this.state.gridOptions}>
</AgGridReact>
</div>
</div>
);
}
}
TasksGridContainer.propTypes = {
listOfTasks: PropTypes.array,
actions: PropTypes.object
};
const mapStateToProps = ({ taskView }) => {
return {
taskView: {
taskGrid: {
listOfTasks: taskView.taskGrid.listOfTasks,
myTaskColumns: taskView.taskGrid.myTaskColumns,
teamTaskColumns: taskView.taskGrid.teamTaskColumns
}
}
}
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(taskGridActions, dispatch)
};
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(TasksGridContainer);
columnDefs are being set once props.taskView.taskGrid.myTaskColumns is available.
a sample columndef:
[
{
cellRenderer: "loadingRenderer", checkboxSelection: true, field: "action", headerCheckboxSelection: true, headerCheckboxSelectionFilteredOnly: true, headerName: "Action"
},
{
"activity"headerName: "Activity Name"
}
]
Although grid is loading fine, but when i scroll it should call "loadingRenderer" component. But,I'm not able to see any loading gif when i scroll.
Am i doing something wrong in implementation?
Actual issue was not calling the the props properly and was not having onGridReady function to use gridAPi.
I modified the code and it starts working:
<AgGridReact
components={this.state.components}
enableColResize={true}
rowBuffer={this.state.rowBuffer}
debug={true}
rowSelection={this.state.rowSelection}
rowDeselection={true}
rowModelType={this.state.rowModelType}
paginationPageSize={this.state.paginationPageSize}
cacheOverflowSize={this.state.cacheOverflowSize}
maxConcurrentDatasourceRequests={this.state.maxConcurrentDatasourceRequests}
infiniteInitialRowCount={this.state.infiniteInitialRowCount}
maxBlocksInCache={this.state.maxBlocksInCache}
columnDefs={this.props.columns}
rowData={this.props.rowData}
onGridReady={this.onGridReady}
>
</AgGridReact>
state :
this.state = {
components: {
loadingRenderer: function(params) {
if (params.value !== undefined) {
return params.data.action;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
rowBuffer: 0,
rowSelection: "multiple",
rowModelType: "infinite",
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2
};
onGridReady function :
onGridReady = (params, data = []) => {
this.gridApi = params;
this.gridColumnApi = params.columnApi;
this.updateData(params,data);
}
Is there a way to "simulate" pressing the refresh button to refresh a List? I have a list that I want it to update every 10 seconds. Is there a way to "press" the refresh button every 10 seconds?
My list name is ActiveJobsList.
This is what I have at the moment:
export function autoRefresh() {
var counter = 10;
var id;
if(location.href.includes("activejobs")) {
id = setInterval(function() {
counter--;
if(counter < 0 && location.href.includes("activejobs")) {
// What should go here?
clearInterval(id);
}
}, 1000);
}
else if (!location.href.includes("activejobs"))
{
clearInterval(id);
}
}
Okay so I managed to figure it out.
I used
var x = document.getElementsByTagName('button');
console.log(x);
To figure out which button corresponded to the refresh button for admin-on-rest. In my case, it was the second button in the array.
Here is my updated code.
export function autoRefresh() {
var counter = 30;
var id;
if(location.href.includes("activejobs")) {
id = setInterval(function() {
counter--;
if(counter < 0 && location.href.includes("activejobs")) {
document.getElementsByTagName('button')[1].click();
counter = 30;
}
}, 1000);
}
else if (!location.href.includes("activejobs"))
{
counter = 30;
}
}
You could leverage React.Component.shouldComponentUpdate(), on your ActiveJobsList
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
I have created a component that provides a drop down menu for auto update setting. Here is the code and below it is an example of how to invoke it.
class AutoUpdt extends Component {
static propTypes = { setAutoUpdate : PropTypes.func
, interval : PropTypes.array
, iconColor : PropTypes.any
}
static defaultProps = { interval : [10,30,60,120,300,600,900,1800,3600]
, iconColor : '#00bcd4'
}
constructor(props) { super(props)
this.state = { open : false
, needrefresh : false
, intervaltime : false
}
}
handleTouchTap(event) { event.preventDefault()
this.setState({ open: true, anchorEl: event.currentTarget, })
}
handleRequestClose() { this.setState({ open: false, })
}
handleShow(event) { let intervaltime = event.currentTarget.innerText.toLowerCase().split(' (secs)')[0].trim()
let newintevaltime = (this.state.intervaltime === false) ? intervaltime : false
this.props.setAutoUpdate( newintevaltime )
this.setState({ open: false, needrefresh: true, intervaltime : newintevaltime})
}
render() {
return ( <div style={{ display: 'inline-block' }}>
<IconButton tooltip="Set Auto Update"
iconStyle={{ color: this.props.iconColor }}
onTouchTap={this.handleTouchTap.bind(this)} ><AutoIcon /></IconButton>
<Popover open={this.state.open}
anchorEl={this.state.anchorEl}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
onRequestClose={this.handleRequestClose.bind(this)} >
<Menu>
{this.props.interval.map( el =>
<ListItem style={( el.toString() !== this.state.intervaltime )
? { color:'#00bcd4' , margin: 0, padding : 2 }
: { color: '#f48fb1' , margin: 0, padding : 2 } }
data-key={ el.toString()}
key={el.toString()}
primaryText={ el.toString() + ' (secs)'}
onTouchTap={this.handleShow.bind(this)} /> )}
</Menu >
</Popover>
</div>)
}
}
// It is invoked by using these two functions in another component
checkMounted(){ this.props.checkMounted && this.props.checkMounted() && this.updateData()
}
setAutoUpdate = ( intervaltimer, checkMounted) => {
const this_ = this
this.state.intervaltimer && clearInterval(this.state.intervaltimer)
this.setState( intervaltimer ? { intervaltimer : setInterval( this_.checkMounted.bind(this_), +intervaltimer * 1000) } : { intervaltimer : false} )
}
// And using this line in the render function of the calling component
{ this.props.hasAuto && <AutoUpdt setAutoUpdate={this.setAutoUpdate} icon={<NavigationRefresh />} /> }
I'm trying to validate a form with tags, where the list must contain at least one tag to be valid. But it's only evaluating when the page loads, and not updating.
https://plnkr.co/edit/umnhybKhNEjswrUJGh3q?p=preview
Validation Function:
function notEmpty(control) {
if(control.value == null || control.value.length===0) {
return {
notEmpty: true
}
}
return null;
}
Component and Template using the Validator:
#Component({
selector: 'my-app',
template: `
<form [formGroup]="myForm">
<div>
(Comma Separated, no duplicates allowed. Enter also submits a tag.)
<div tags formControlName="list" [(ngModel)]="list"> </div>
<div *ngIf="myForm.get('list').valid">List 1 Not Empty.</div>
<div tags formControlName="list2" [(ngModel)]="list2"> </div>
<div *ngIf="myForm.get('list2').valid">List 2 Not Empty.</div>
</div>
</form>
`,
})
export class App {
list:Array<string>;
list2:Array<string>;
myForm:FormGroup;
myList:FormControl;
myList2:FormControl;
constructor(private fb: FormBuilder) {
this.list = [];
this.list2 = ["test"];
this.myList = fb.control('', notEmpty);
this.myList2 = fb.control('', notEmpty);
this.myForm = fb.group({
list: this.myList,
list2: this.myList2
});
}
addItem(item:string) {
this.list.push(item);
}
}
Tags Component and other child components:
const MY_PROVIDER = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(()=> Tags),
multi: true
};
#Component({
selector: 'tags, [tags]',
template: `
<div>
<tag-item *ngFor="let item of tagsList" item="{{item}}" (remove)="removeTag(item)"></tag-item>
<input class="tagInput" #tagInput
(focus)="focus()"
[(ngModel)]="current"
(keydown)="keyDown($event)"
(keyup)="keyUp($event)"
(blur)="blur()"
placeholder="+ Tag"/>
</div>
`,
providers: [MY_PROVIDER]
})
export class Tags implements ControlValueAccessor {
tagsList : Array<string>;
current : string;
#ViewChild('tagInput') child;
inFocus : boolean = false;
constructor() {
this.current = "";
this.tagsList = new Array<string>();
}
focus() {
this.child.nativeElement.focus();
this.inFocus = true;
}
keyDown(event:KeyboardEvent) {
if (event.keyCode === 188 || event.keyCode === 13) { //188 is Comma, 13 is Enter, 32 is Space.
this.pushTag();
} else if (event.keyCode === 8 && this.current.length == 0 && this.tagsList.length > 0){
this.current = this.tagsList.pop();
}
}
keyUp(event:KeyboardEvent) {
if(event.keyCode === 188) {
this.current = '';
}
}
pushTag() {
let str = this.current;
this.current = '';
if(str.trim() != '') {
for(let s of str.split(',')) {
s = this.sanitize(s);
if(s.trim() != '') {
if(!this.tagsList.some(x => x.toLowerCase() === s.toLowerCase()))
this.tagsList.push(s);
}
}
}
}
sanitize(str: string) : string {
let s = str;
s = s.replace('\'', '').replace('"', '').replace(';', '');
return s;
}
blur() {
this.pushTag();
this.inFocus = false;
}
removeTag(value) {
let index = this.tagsList.indexOf(value, 0);
if (index > -1) {
this.tagsList.splice(index, 1);
}
}
clear() {
this.tagsList = new Array<string>();
}
get value(): Array<string> { return this.tagsList; };
set value(v: Array<string>) {
if (v !== this.tagsList) {
this.tagsList = v;
this.onChange(v);
this.onTouched();
}
}
writeValue(value: Array<string>) {
this.tagsList = value;
this.onChange(value);
}
onChange = (_) => {};
onTouched = () => {};
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
#Component({
selector: 'tag-item, [tag-item]',
template: `{{item}} <delete-me (click)="removeTag(item)">x</delete-me>`
})
export class TagItem {
#Input() item : string;
#Output() remove : EventEmitter<string> = new EventEmitter();
removeTag(item) {
this.remove.emit(item);
}
}
#Component({
selector:'delete-me',
template:'x'
})
export class DeleteIcon {
}
This seems to work.
...//Tags Component pushMethod
pushTag() {
let str = this.current;
this.current = '';
if(str.trim() != '') {
for(let s of str.split(',')) {
s = this.sanitize(s);
if(s.trim() != '') {
if(!this.tagsList.some(x => x.toLowerCase() === s.toLowerCase())) {
this.tagsList.push(s);
this.pushed.emit(s); // created an EventEmitter<string>
}
}
}
}
}
And in my main component:
#Component({
selector: 'my-app',
template: `
<form [formGroup]="myForm">
<div>
(Comma Separated, no duplicates allowed. Enter also submits a tag.)
<div tags formControlName="list" (pushed)="update()" [(ngModel)]="list"> </div>
<div *ngIf="myForm.get('list').valid">List 1 Not Empty.</div>
<div tags formControlName="list2" (pushed)="update()" [(ngModel)]="list2"> </div>
<div *ngIf="myForm.get('list2').valid">List 2 Not Empty.</div>
</div>
</form>
`,
})
export class App {
list:Array<string>;
list2:Array<string>;
myForm:FormGroup;
myList:FormControl;
myList2:FormControl;
constructor(private fb: FormBuilder) {
this.list = [];
this.list2 = ["test"];
this.myList = fb.control('', notEmpty);
this.myList2 = fb.control('', notEmpty);
this.myForm = fb.group({
list: this.myList,
list2: this.myList2
});
}
update() {
this.myList.updateValueAndValidity();
this.myList2.updateValueAndValidity();
}
addItem(item:string) {
this.list.push(item);
}
}
Seems kind of hacky, though, so I'd still like a better answer, if there is one.