Vuetify: Child component(NumberField) value set from parent component programmatically is showing required error even with valid value - vuetify.js

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

axios post without refresh page

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

GraphQL relay connectionFromArraySlice

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))
},

infinite scrolling using AgGridReact

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?

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 />} /> }

Custom Angular2 Validator only evaluates when the page loads, and not when the object is updated

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.

Resources