This commit is contained in:
@@ -14,27 +14,16 @@ TODO CLIENT STUFF
|
||||
|
||||
TODO NEXT
|
||||
|
||||
End to end action
|
||||
- The faster I get to a fully working basic level like being able to do data entry and submit to server new or updated records, the faster I can flesh out all the rest
|
||||
- Code the submit data to server route for update
|
||||
- Code for new record to the server
|
||||
|
||||
GZ VALIDATE
|
||||
- Add rules for each field type validation
|
||||
- Is there anything pulled into the validate code directly that is already available off the "v" variable?
|
||||
- localization etc
|
||||
Simulate server broken rules so can integrate into ui and rule system
|
||||
|
||||
Going to need to handle localized numeric entry formats
|
||||
|
||||
|
||||
DATETIME
|
||||
|
||||
- Validation
|
||||
- after much fuckery it's becoming clear that between the localization requirements and the complexity of server broken rules coming back from the api that I should just use my own validation code
|
||||
- See here: https://vuetifyjs.com/en/components/forms#form
|
||||
- And here: https://vuetifyjs.com/en/components/text-fields#custom-validation
|
||||
- What is needed is a class that returns an array of functions that can be passed to vuetify "rules" property of components
|
||||
On change each rule is called in turn and if one returns false or a string then it's in an error state
|
||||
- I also need to add that it scans and or keeps a collection of broken rules that come back from the server and each component checks a rule that checks in turn for server broken rules in last api call
|
||||
- And my rules object needs to be localized so I guess it should be aware of the locale object and work with it as required
|
||||
- Should it lazy load error translations? (In as batchy a way as possible?)
|
||||
- Or, perhaps it's more convenient to cache all possible regular form entry errors since there isn't really that many, perhaps less than a dozen when it comes down to it
|
||||
- My rules object needs to be coded so I can easily specify a collection of rules appropriate to a form field
|
||||
- So, for example let's say I have a date input, if I commonly need one to be required and be after or before another field then I should have a single combined rule of
|
||||
RequiredAndAfterTarget and this method is aggregates both the required and the After rules into a single collection returned
|
||||
@@ -46,9 +35,16 @@ DON'T code the user options with the currency symbol etc until after it's all be
|
||||
Locale should fetch those settings the first time it sees they are not present so that they are refreshed upon use and are not stored in localstorage
|
||||
(or should they be? anyway, can work that out later)
|
||||
|
||||
|
||||
|
||||
Other fields that are locale dependent
|
||||
DateTime field next
|
||||
|
||||
|
||||
|
||||
|
||||
FIXES REQUIRED
|
||||
|
||||
- API get code is incorrectly dealing with expired bearer cert, a 401 is returned and it tries to parse the result as if it succeeded when it really should trigger a login process
|
||||
- Time zone offset mismatch warning needs expansion, it should only prompt a few times (maybe or find a way to deal with this) and it should offer to change it at the server automatically
|
||||
- Localize time zone mismatch warning
|
||||
@@ -160,6 +156,10 @@ Make all fields work according to specs below
|
||||
- What to do if they edit? Refresh the list but keep the same page location?
|
||||
|
||||
|
||||
TESTING TODO
|
||||
Localized input and parsing and validation
|
||||
- Going to need to handle localized numeric entry formats
|
||||
- Deal with this once form is in good shape and worth testing with different locales
|
||||
-----------------
|
||||
|
||||
TODO AFTER CLIENT block ABOVE:
|
||||
|
||||
@@ -93,6 +93,14 @@ export default {
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
},
|
||||
fetchPutOptions(data) {
|
||||
return {
|
||||
method: "put",
|
||||
mode: "cors",
|
||||
headers: this.postAuthorizedHeaders(),
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
},
|
||||
fetchGetOptions() {
|
||||
/* GET WITH AUTH */
|
||||
return {
|
||||
@@ -134,6 +142,9 @@ export default {
|
||||
}
|
||||
return store.state.apiUrl + apiPath;
|
||||
},
|
||||
/////////////////////////////
|
||||
// ENCODE QUERY STRING
|
||||
//
|
||||
buildQuery(obj, sep, eq, name) {
|
||||
sep = sep || "&";
|
||||
eq = eq || "=";
|
||||
@@ -166,6 +177,9 @@ export default {
|
||||
encodeURIComponent(stringifyPrimitive(obj))
|
||||
);
|
||||
},
|
||||
///////////////////////////////////
|
||||
// GET DATA FROM API SERVER
|
||||
//
|
||||
get(route) {
|
||||
var that = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
@@ -195,6 +209,49 @@ export default {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
///////////////////////////////////
|
||||
// POST / PUT DATA TO API SERVER
|
||||
//
|
||||
upsert(route, data) {
|
||||
var that = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
//determine if this is a new or existing record
|
||||
var fetchOptions = undefined;
|
||||
if (data.concurrencyToken) {
|
||||
fetchOptions = that.fetchPutOptions(data);
|
||||
} else {
|
||||
fetchOptions = that.fetchPostOptions(data);
|
||||
}
|
||||
fetch(that.APIUrl(route), fetchOptions)
|
||||
.then(that.status)
|
||||
.then(that.json)
|
||||
.then(response => {
|
||||
//Note: response.error indicates there is an error, however this is not an unusual condition
|
||||
//it could be validation errors or other general error so we need to treat it here like it's normal
|
||||
//and let the caller deal with it appropriately
|
||||
resolve(response);
|
||||
})
|
||||
.catch(function(error) {
|
||||
//fundamental error, can't proceed with this call
|
||||
|
||||
var errorMessage =
|
||||
"API error: UPSERT route =" + route + ", message =" + error.message;
|
||||
store.commit("logItem", errorMessage);
|
||||
|
||||
if (error.message && error.message.includes("401")) {
|
||||
store.commit(
|
||||
"logItem",
|
||||
"User is not authorized, redirecting to login"
|
||||
);
|
||||
auth.logout();
|
||||
router.push("/login");
|
||||
} else {
|
||||
//alert("Error: " + errorMessage);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//new functions above here
|
||||
|
||||
@@ -75,6 +75,46 @@ function getControlLabel(ctrl) {
|
||||
}
|
||||
|
||||
export default {
|
||||
///////////////////////////////
|
||||
// SERVER ERRORS
|
||||
//
|
||||
Server(v, ref) {
|
||||
|
||||
|
||||
//check if any errors and short circuit if none
|
||||
|
||||
//check if this control is mentioned at all and short circuit return false if not
|
||||
|
||||
var ctrl = getControl(v, ref);
|
||||
if(typeof ctrl == 'undefined'){
|
||||
return false;
|
||||
}
|
||||
|
||||
//It IS in the list of error controls:
|
||||
|
||||
//check if this control has been changed at all, I guess being here might mean it has?
|
||||
//If this control is dirty / was changed from the value that was in it when the server returned the error then remove the error for this control from the server error list and return false
|
||||
//ELSE if this control is unchanged / not dirty
|
||||
//return the error(s) properly localized
|
||||
|
||||
|
||||
//IF user modifies field in any way then this rule must be cleared
|
||||
|
||||
//Some rules are for the entire object so...???
|
||||
//maybe need a form control that is just about rules or something
|
||||
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
// // "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
||||
// var err = locale.get("ErrorRequiredFieldEmpty");
|
||||
// var fieldName = getControlLabel(ctrl);
|
||||
// err = _.replace(err, "{0}", fieldName);
|
||||
// //lodash replace only replaces first instance so need to do it twice
|
||||
// err = _.replace(err, "{0}", fieldName);
|
||||
// return err;
|
||||
},
|
||||
///////////////////////////////
|
||||
// REQUIRED
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<v-layout v-if="this.formReady">
|
||||
<v-flex>
|
||||
<form ref="form">
|
||||
<v-form ref="form">
|
||||
<v-layout align-center justify-left row wrap>
|
||||
<v-flex xs12 sm6 lg4 xl3 px-2>
|
||||
<v-text-field
|
||||
@@ -26,7 +26,7 @@
|
||||
v-model="obj.count"
|
||||
:counter="10"
|
||||
:label="this.$gzlocale.get('WidgetCount')"
|
||||
ref="count"
|
||||
ref="count"
|
||||
:rules="[this.$gzv.Integer(this,'count'),this.$gzv.Required(this,'count')]"
|
||||
required
|
||||
></v-text-field>
|
||||
@@ -47,7 +47,7 @@
|
||||
<gz-date-time-picker
|
||||
:label="this.$gzlocale.get('WidgetStartDate')"
|
||||
v-model="obj.startDate"
|
||||
ref="startDate"
|
||||
ref="startDate"
|
||||
></gz-date-time-picker>
|
||||
</v-flex>
|
||||
|
||||
@@ -63,24 +63,34 @@
|
||||
<v-checkbox
|
||||
v-model="obj.active"
|
||||
:label="this.$gzlocale.get('Active')"
|
||||
ref="active"
|
||||
ref="active"
|
||||
required
|
||||
></v-checkbox>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6 lg4 xl3 px-2>
|
||||
<v-text-field
|
||||
v-model="obj.roles"
|
||||
:label="this.$gzlocale.get('WidgetRoles')"
|
||||
ref="roles"
|
||||
:rules="[this.$gzv.Integer(this,'roles'),this.$gzv.Required(this,'roles')]"
|
||||
:error-messages="canHasServerError('roles')"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
||||
<v-layout align-center justify-space-around row wrap mt-5>
|
||||
<v-flex xs1>
|
||||
<v-btn>one</v-btn>
|
||||
<v-btn @click="validate">Force validate</v-btn>
|
||||
</v-flex>
|
||||
<v-flex xs1>
|
||||
<v-btn>two</v-btn>
|
||||
<v-btn>test2</v-btn>
|
||||
</v-flex>
|
||||
<v-flex xs1>
|
||||
<v-btn @click="submit">submit</v-btn>
|
||||
<v-btn @click="submit">save</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</form>
|
||||
</v-form>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</template>
|
||||
@@ -133,10 +143,27 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
obj: {},
|
||||
serverErrors: {},
|
||||
formReady: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getErrorCount() {
|
||||
return 3;
|
||||
},
|
||||
canHasServerError(fieldName) {
|
||||
if (fieldName == "roles") {
|
||||
return ["This is an error"];
|
||||
}
|
||||
},
|
||||
validate() {
|
||||
// this.$refs.form.resetValidation();
|
||||
// this.$refs.form.validate();
|
||||
//test to manually insert an error into the field like a server validation error would need to do
|
||||
//UPDATE: this cannot work because you need to bind to error-messages, not set it directly like here
|
||||
// this.$refs.roles["error-messages"] = ["This is an error!"];
|
||||
// this.$refs.roles.error = true;
|
||||
},
|
||||
getDataFromApi() {
|
||||
var url = "Widget/" + this.$route.params.id;
|
||||
this.$gzapi.get(url).then(res => {
|
||||
@@ -145,9 +172,61 @@ export default {
|
||||
},
|
||||
submit() {
|
||||
// debugger;
|
||||
//this.$validator.validateAll();
|
||||
this.$refs.form.validate();
|
||||
}
|
||||
//Submit the form data to the api
|
||||
|
||||
//Check for broken rules, do not submit with broken rules
|
||||
//No broken rules then:
|
||||
//Gather up the form data into a submitable object (can I just submit the existing object??)
|
||||
//Post it
|
||||
//Update the local object with the returned result
|
||||
//Check the return object for broken rules
|
||||
var that = this;
|
||||
var url = "Widget/" + this.$route.params.id;
|
||||
this.$gzapi
|
||||
.upsert(url, this.obj)
|
||||
.then(res => {
|
||||
if (res.error) {
|
||||
debugger;
|
||||
that.serverErrors = res.error;
|
||||
that.$refs.form.resetValidation();
|
||||
that.$refs.form.validate();
|
||||
//example error when submit when there are no roles set at all (blank)
|
||||
//{"error":{"code":"2200","details":[{"code":"2200","message":"","target":"roles","error":"VALIDATION_FAILED"}],"message":"Object did not pass validation"}}
|
||||
//todo: integrate validation error into form so user can see it
|
||||
//User can only submit if no existing errors so should be starting with a clean error free form and then will see validation errors under appropriate fields
|
||||
//if a user makes a change to a control then that control being changed should remove the rule from the server until the next submit happens
|
||||
} else {
|
||||
//Logic for detecing if a post or put: if id then it was a post, if no id then it was a put
|
||||
if (res.id) {
|
||||
//Handle "post" of new record
|
||||
that.obj = res.data;
|
||||
} else {
|
||||
//Handle "put" of an existing record
|
||||
that.obj.concurrencyToken = res.data.concurrencyToken;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
//we should be here if it was a gross error of some kind not a mild one like validation but more like if the server doesn't exist or something I guess
|
||||
|
||||
console.log(error);
|
||||
alert("Houston, we have a problem");
|
||||
});
|
||||
|
||||
//example from login form
|
||||
// if (this.input.username != "" && this.input.password != "") {
|
||||
// auth
|
||||
// .authenticate(this.input.username, this.input.password)
|
||||
// .then(() => {
|
||||
// this.$router.replace({ name: "home" });
|
||||
// })
|
||||
// .catch(function(error) {
|
||||
// /* xeslint-disable-next-line */
|
||||
// //console.log(error);
|
||||
// alert("login failed: " + error);
|
||||
// });
|
||||
// }
|
||||
} //end of submit()
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user