I'm building something like this, where the country I pick will dictate the phone country code automatically.
Both the country and countryCode are stored in a customer object, and when I'm changing the country's value, the trigger is correctly called and I can see the country code changing in Vue dev tools, however the related input does not update. This is my code:
data: function () {
return {
customer: {},
countries: this.$store.state.settings.countries,
}
},
created: function() {
var defaultCountry = _.find(this.countries, { default: true });
this.customer.country = defaultCountry.name;
this.customer.countryCode = defaultCountry.code;
},
methods: {
updateCountryCode: function(country) {
this.customer.countryCode = country.code;
},
}
And this is the relevant HTML:
<vSelect
label="name"
v-model="customer.country"
:options="countries"
:onChange="updateCountryCode">
</vSelect>
<input type="text" disabled :value="customer.countryCode">
What am I doing wrong? Why do I see the data being updated on dev tools but it doesn't act as reactive and my country code input stays the same?
You should define your customer object like so customer:
{
country: null, // or some other default value
countryCode: null,
},
You can find more details in the documentation here
This is how you shoul do it or #Nora's method of initializing the object properties to null
updateCountryCode: function(country) {
this.$set(this.customer, 'countryCode', country.code)
},
And the reason is because of change detection caveats in vue reactivity
Related
I have an Array of statuses objects. Every status has a name, and a boolean set at false by default.
It represent checkbox in a form with filters, when a checkbox is checked bool is set at true :
const filters.statuses = [
{
name: "pending",
value: false
},
{
name: "done",
value: false
},
];
I am using Angular HTTP Params to pass params at the URL.
filters.statuses.forEach((status) => {
if (status.value) {
this.urlParams = this.urlParams.append('statuses[]', status.name);
}
});
Url params looks like when a status is checked :
&statuses%5B%5D=pending
My problem is when I want to unchecked.
I know HTTP Params is Immutable, so, I'm trying to delete the param when checkbox is unchecked, so set to false :
...else {
this.urlParams = this.urlParams.delete('statuses');
}
But, it not works, URL doesn't change.
And if I re-check to true after that, the URL looks like :
&statuses%5B%5D=pending&statuses%5B%5D=pending
How can I delete params, if the status value is false, and keep others statuses in URL ?
Project on Angular 10.
Thanks for the help.
UPDATE : It works to delete, my param name was not good :
else {
this.urlParams = this.urlParams.delete('statuses[]', status.name);
}
But, my other problem, it's when I check 2 or more checkbox, the append function write on URL : &statuses%5B%5D=pending&statuses%5B%5D=pending&statuses%5B%5D=done
I have prepared an example to try to answer your question (If I understand this right way).
You can change the checkboxes state or the URL to play with it. Also, I added helper buttons, which will navigate you to different cases (by changing the URL).
Here is the example: https://stackblitz.com/edit/angular-router-basic-example-cffkwu?file=app/views/home/home.component.ts
There are some parts. We will talk about HomeComponent.
You have ngFor which displays statuses, I handled state using ngModel (you can choose whatever you want).
You have a subscription to the activatedRoute.queryParams observable, this is how you get params and set up checkboxes (the model of the checkboxes)
You have the ngModelChange handler, this is how you change the route according to the checkboxes state
Let's focus on 2 & 3 items.
The second one. Rendering the correct state according to the route query params. Here is the code:
ngOnInit() {
this.sub = this.activatedRoute.queryParams.subscribe((params) => {
const statusesFromParams = params?.statuses || [];
this.statuses = this.statuses.map((status) => {
if (statusesFromParams.includes(status.name)) {
return {
name: status.name,
active: true,
};
}
return {
name: status.name,
active: false,
};
});
});
}
Here I parse the statuses queryParam and I set up the statuses model. I decide which is active and which is not here.
The third one. You need to update the URL according to the checkboxes state. Here is the code:
// HTML
<ng-container *ngFor="let status of statuses">
{{ status.name}} <input type="checkbox" [(ngModel)]="status.active" (ngModelChange)="onInputChange()" /> <br />
</ng-container>
// TypeScript
onInputChange() {
this.router.navigate(['./'], {
relativeTo: this.activatedRoute,
queryParams: {
statuses: this.statuses
.filter((status) => status.active)
.map((status) => status.name),
},
});
}
Here you have the ngModelChange handler. When any checkbox is checked/unchecked this handler is invoked. In the handler, I use the navigate method of the Router to change the URL. I collect actual checkboxes state and build the query parameters for the navigation event.
So, now you have a binding of the checkboxes state to the URL and vice versa. Hope this helps.
I am using VueJS with Firebase. I am trying to update data using a form. In the form, I am able to get old data in almost all fields. There is an autocomplete field, in which I am not to display the fetched data. The data is present in initialDisplay but not display. It seems like the object is not fetched.
Here is my code
<CustomerAutoComplete
description="Select customer associated with this project"
label="Customer"
placeholder="Customers"
#selected="clientSelected"
:initialValue="projectForm.customerId"
:initialDisplay="customer"
v-model="projectForm.customerId"
/>
props: {
customer: {
type: Object
}
},
data: function () {
return {
projectForm: {
customerId: '',
address: ''
} as Project
}
},
methods: {
clientSelected ({ selectedObject }) {
this.projectForm.customerId = selectedObject.id
if (selectedObject.address) this.projectForm.address = selectedObject.address
},
},
Here is what I can from the browser console in the Vue tab.
{"id":"op234Sd3FBZHwfLpk16n","companyId":"zErEDd6jE4a0H6bY8u77","name":"Test Customer","email":"test#customer.com"}
In data section, I can Object but no data.
I feel like I am missing something small, no idea what it is.
Thanks in advance for your help!
I am making a meteor web app where the user will click on a html button. Once this button is clicked, the user needs to be directed to another page with some forms generated by a meteor simple schema package. The first field in the simple schema needs to automatically be given a string value of "hello" and then the rest of the fields in the simple schema will be filled out by the user with the input fields on the page. What I am unsure about is how to get the first value automatically set to this string value. Here is some of the code I have:
The simple schema declaration:
LobbySchema = new SimpleSchema({
game: {
type: String,
label: "Game"
},
console: {
type: String,
label: "Console"
},
players: {
type: Number,
label: "Players"
},
mic: {
type: Boolean,
label: "Mic"
},
note: {
type: String,
label: "Note"
},
gamertag: {
type: String,
label: "Gamertag"
},
createdAt: {
type: Date,
label: "Created At",
autoValue: function(){
return new Date()
},
autoform: {
type: "hidden"
}
}
});
The first field there in the schema "game" needs to be given the value "hello" when the html button is clicked. Right now I can assign that value to a javascript variable using the button by having an onclick function:
function getElementText(elementID){
var elementText = "hello";
}
The button would call the getElementText function and have the elementText variable equal "hello". Now I need to assign the the first field in the simple schema to this variable value, "hello", then have it so the user can now fill out the rest of the schema with the input fields, automatically generated into the html with this code:
{{> quickForm collection="Lobby" id="insertLobbyForm" type="insert" class="newLobbyForm"}}
If you do not feel like providing the answer (maybe it happens to be more complicated than I think) then I would be very happy to receive a link to a site that might help me with this. I am also very willing to explain anything about the question if I did not explain the situation well enough above.
You can use the AutoForm hooks like this:
AutoForm.hooks({
app_create: {
before: {
method: function (doc) {
// Do whatever assignment you need to do here,
// like doc['game'] = "hello"; //then
return doc;
}
},
onSuccess: function (formType, result) {
},
onError: function (formType, error) {
}
}
});
Where here app_create is the id of the form you're sending with Autoform.
I'm using the excellent parse-react library to get Parse and ReactJS to work together nicely (n.b I've only been playing around for a few hours so apologies if I've misunderstood any of the basics of reactjs).
All was going well until I wanted to query a table for all objects created by the current user (Parse.user.current())
The observe method works correctly on load and the view is rendered with the correct objects (the objects created by the current user). However if I mutate the data and add a new object then the view doesn't re-render.
Abstracted code:
module.exports = React.createClass({
mixins: [ParseReact.Mixin],
getInitialState: function() {
return {
selected: null
};
},
observe: function() {
return {
places: (new Parse.Query('Place'))
.equalTo('user', Parse.User.current())
.descending('createdAt')
};
},
clickHandler: function(event) {
var id = event.target.id;
if (id === 'new') {
ParseReact.Mutation.Create('Place', {
name: 'New Place',
user: Parse.User.current()
}).dispatch();
} else if(id.indexOf('Place:') === 0) {
this.setState({
selected: id.substring(6)
});
}
},
render: function() {
var that = this;
var navItems = this.data.places.map(function(place) {
return (
<UserNavItem id={place.id} key={place.id} label={place.name} selected={that.state.selected === place.objectId} onClick={that.clickHandler}/>
);
});
return (
<ul>
{navItems}
<UserNavItem id='new' label='+ New Place' onClick={this.clickHandler} />
</ul>
);
}
});
If I remove the part of the query that specifies the user:
.equalTo('user', Parse.User.current())
Then it works; new place objects appear in the list when added.
Has anyone got any idea what I'm doing wrong?
Am I using Parse queries incorrectly? It always seems strange that getting the data pertaining to the current user is a bit of a pain when this seems like such a common use case?
Thanks!
The solution is to call the .refreshQueries() method of the component when the new object is successfully created in Parse as described here.
My updated example:
ParseReact.Mutation.Create('Place', {
name: 'New Place',
user: Parse.User.current()
})
.dispatch()
.then(function() {
this.refreshQueries();
}.bind(this));
Thanks very much to Joshua Sierles over on the ParseReact github repo for pointing me to the solution. If you are on SO Joshua I'll give you the credit if you post your answer here :D
I'm wondering about the most efficient, safe and smart way to code a complex form in javascript.
Often a user form can be very complex with a lot of different states, complex checks and so on, and to do a good job a good concept is absolutely necessary.
I really believe that the best solution is a state machine.
The following code is the form core logic I did for user registration with an RFID tag key, where user can register themselves using different credentials as phone, key RFID tag, mail, and complete their registration at a later stage with the same forum.
Consider just the logic behind it at high level.
The main loop which iterates on possible transitions (in order of priority):
/* Evaluate the actual form state (status) and check if it's possible to change
* to another status with a greater priority.
* If the state transition conditions are verified the transition callback is executed
* which, in case the state doesn't, is an empty function.
*/
setNextFormStatus: function(field) {
var sts,
that = this;
function conditionsAreValid(sts) {
var context = that.statusToConditionsMap[sts];
return ( sts == 'new_account' || that[context.field].state == context.state );
}
for (sts in this.statusToConditionsMap[this.status.actual].changes) {
var transition = this.statusToConditionsMap[this.status.actual].changes[sts];
if (transition && conditionsAreValid(sts)) {
if (sts != this.status.actual) {
this.status.previous = this.status.actual;
this.status.actual = sts;
this._resetForm(); // simple reset function
transition.apply(this);
}
break;
}
}
}
All status, their transition conditions, and their transition callbacks are defined in a dictionary when status are listed in order of priority:
/*
* This is the dictionary which defines form status states machine
*
* For each status are defined the following attributes:
* · state & field: define the condition to enter this status. The field must have that state (i.e. field.state == state)
* · changes (optional): the list of possible next status from this one, ordered by priority. Each status has an handle to call
*/
this.statusToConditionsMap = {
'registered_key':{
state: 'registered',
field: 'key'
},
'processed_key_with_username':{
state: 'unlinked',
field: 'key',
changes: {
'processed_key_with_username': function() {}, // empty cause callback is unnecessary
'new_account': this._checkNotEmptyFields
}
},
'phone_already_present_confirmed':{
state: 'already_present_confirmed',
field: 'phone',
changes: {
'phone_already_present_confirmed': function() {},
'phone_already_present_unconfirmed': this.phone_already_present_unconf_data_filler,
'new_account': this._checkNotEmptyFields
}
},
'phone_already_present_unconfirmed':{
state: 'already_present_unconfirmed',
field: 'phone',
changes: {
'phone_already_present_confirmed': this.phone_already_present_data_filler,
'phone_already_present_unconfirmed': function() {},
'new_account': this._checkNotEmptyFields
}
},
'email_already_present':{
state: 'email_already_present',
field: 'email',
changes: {
'phone_already_present_confirmed': this.phone_already_present_data_filler,
'phone_already_present_unconfirmed': this.phone_already_present_unconf_data_filler,
'email_already_present': function() {},
'new_account': this._checkNotEmptyFields
}
},
'new_account':{
state:'',
field: '',
changes: {
'registered_key': this.registered_key_data_filler,
'processed_key_with_username': this.processed_desikey_data_filler,
'phone_already_present_confirmed': this.phone_already_present_data_filler,
'phone_already_present_unconfirmed': this.phone_already_present_unconf_data_filler,
'email_already_present': function() {this.showMailCheckbox(); this.runCheck('phone');},
'new_account': function() {}
}
}
}
Which can be a best practice to implement a complex form?
Any other solution or method will be appreciated.