Updating model value onChange in Meteor + React places cursor at the end of the string - meteor

I am using Meteor with React. Consider this simple component below. There is a local mini-Mongo collection CommentsCollection. The component will insert a row in it when componentWillMount will be called. getMeteorData will return the first record in the collection and we'll be able to modify the title. Problem: if I place my cursor at the start of the title and start typing, after the first character update the cursor will jump to the end of the string and the rest of my typing will be placed there. How do I work around this?
CommentsCollection = new Meteor.Collection(null); // Local mini-mongo collection
EventTestComponent = React.createClass({
mixins : [ReactMeteorData],
componentWillMount(){
CommentsCollection.insert({title:"test title", message:"some test message"});
},
getMeteorData(){
return {
comment: CommentsCollection.findOne()
};
},
handleTitleChange(e){
CommentsCollection.update({_id: this.data.comment._id}, {$set:{title: e.target.value}});
},
render(){
if(this.data.comment) {
return (
<div>
<input type="text" value={this.data.comment.title} onChange={this.handleTitleChange}/>
</div>
);
}else{
return <div>Loading...</div>
}
}
});

I came up with this solution right after I posted the question:
<input type="text"
defaultValue={this.data.comment.title}
onKeyUp={this.handleTitleChange}/>
So: change value to defaultValue, and onChange to onKeyUp. Works like a charm!

Related

Angular Reactive form asynchronous operation

I am getting this error when I try to build a reactive form for creating a new password form.I have mentioned the source code below and when I remove the source code part then there is no error but without that my operation is not working as well. I think I have to add or delete something in my source code to get the desired output
main.ts:12 TypeError: Cannot read property 'value' of null
at FormGroup.passwordShouldMatch [as validator] (password.validators.ts:18)
at FormGroup._runValidator (forms.js:4089)
at FormGroup.updateValueAndValidity (forms.js:4050)
at new FormGroup (forms.js:4927)
at FormBuilder.group (forms.js:8924)
at new ChangePasswordComponent (change-password.component.ts:15)
at createClass (core.js:31987)
at createDirectiveInstance (core.js:31807)
at createViewNodes (core.js:44210)
at callViewAction (core.js:44660)
static passwordShouldMatch(control : AbstractControl) {
let newPassword = control.get('newPassowrd');
let confirmPassword = control.get('confirmPassowrd');
if (newPassword.value !== confirmPassword.value){
return { passwordShouldMatch:true };
return null;
}
}
As you didn't add any code snippet I am considering your form structure is something like this.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
});
here, you include an custom validation function called passwordShouldMatch and this function looks fine. So, I assume that you did something wrong when you set the validator to that form group.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
}, { validator: this.passwordShouldMatch});
this is how you should set the validation function for the form group. And in html your form should be something like this.
<form [formGroup]="form" novalidate (ngSubmit)="onSubmit(survey)">
<input type="text" placeholder="Untitled form" formControlName="newPassowrd">
<input type="text" placeholder="Untitled form" formControlName="confirmPassowrd">
<span *ngIf="form.hasError('passwordShouldMatch')">not match</span>
</form>
everything should work this way. Here is the working version of stackblitz

how to find document in mongodb with its id passed in textbox?

I am learning mongodb with meteor js so dont have much knowledge of both mongodb and meteor.
Below is the js code:
import { Template } from 'meteor/templating';
import { Dbs } from '../lib/collections.js';
import './main.html';
Template.body.helpers({
/*temp1:[
{text:'my data1'}'
{text:'my data1'}
]*/
dbs(){
return Dbs.find({'user_id':'p123'});
}
});
Basically i just want to pass user id in a textbox and based on it,i want to display the other details of user.In above code i am passing it manually and its working.anyone suggest me what should i do here ?
You can attach to the template instance a ReactiveVar that you will update with an event.
Then use it inside the helper, so the helper will re-execute everytime the value change:
Template.myTemplate.onCreated(function() {
this.currentTextBox = new ReactiveVar();
});
Template.myTemplate.events({
"keyup .js-my-textbox"(event, instance) {
// This event is executed when you type in an input with the class "js-my-textbox"
instance.currentTextBox.set(event.target.value);
},
});
Template.myTemplate.helpers({
dbs() {
const instance = Template.instance();
return Dbs.find({'user_id': instance.currentTextBox.get() });
},
});
EDIT:
Example of what could be the html part:
<template name="myTemplate">
<input type="text" placeholder="Search" class="js-my-textbox">
{{#each db in dbs}}
<-- Do what you want, example: -->
<p>{{db.myField}}</p>
{{/each}}
</template>
First You Create Event Function Template.body.events({}) to access text .Then store variable to use Meteor Session. Then Variable easily access helpers.If session changes variable will be changed.

In React, how do I pass input value to callback function?

I'd like to call some callback in a React component whenever the input's value has changed.
simplified example should explain:
in a jsx file:
<input onKeyDown={this.props.changeCallback( VALUE_OF_INPUT )} ></input>
what should VALUE_OF_INPUT be?
Thanks!
There's not much React-specific about this. Use the event which is passed to the callback as the first argument:
onKeyDown={event => this.props.changeCallback(event.target.value)}
This approach (using a fat arrow function) also handles binding this, at the expense of creating a new anonymous function once per render. Note that calling bind essentially does the same thing, so this is my preferred option out of the two.
It's important to make the distinction between assigning a function to the onKeyDown attribute (as is done here) and calling the function (what you did).
<input value={this.state.value} onKeyDown={this.props.changeCallback.bind( this, this.state.value)} ></input>
Would do the job.
Another way to do it dynamic is give it a value callback this works well if pre populating the "value" from some var.
/* Better function for larger forms */
handleChange = (event) => {
var name = event.target.name;
var value = event.target.value;
this.setState({[name]: value});
}
/* set Default and callback to state */
valueCallback = (value, name) => {
if (this.state[name]) {
return this.state[name]
}
else {
return value;
}
}
then you can go ...
var defaut = '1234'; // or this.props.something.something;
<input type={type} name={name} value={this.valueCallback(defaut, name)} onChange={this.handleChange} />

creating element on click in Meteor

I'm using Meteor with React. I have a really simple goal, but i have tried a lot and can't solve it for myself. I will show you my attemps below.
I want to create a form for the Ingredients. At the first moment there is only one input (for only one ingredient) and 2 buttons: Add Ingredient and Submit.
class IngredientForm extends Component {
render() {
return(
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text"/>
{ this.renderOtherInputs() }
<input type="button" value="Add Ingredient" onClick={this.addIngredient.bind(this)}>
<input type="submit" value="Submit Form">
</form>
);
}
}
So when I click Submit then all the data goes to the collection. When I click Add Ingredient then the another text input appears (in the place where renderOtherInputs() ).
I know, that Meteor is reactive - so no need to render something directly. I should underlie on the reactive data storage.
And I know from the tutorials the only one way to render something - I should have an array (that was based on collection, which is always reactive) and then render something for each element of that array.
So I should have an array with number of elements = number of additional inputs. that is local, so I can't use Collection, let's use Reactive Var instead of it.
numOfIngredients = new ReactiveVar([]);
And when I click Add button - the new element should be pushed to this array:
addIngredient(e) {
e.preventDefault();
let newNumOfIngredients = numOfIngredients.get();
newNumOfIngredients.push('lalal');
numOfIngredients.set(newNumOfIngredients);
}
And after all I should render additional inputs (on the assumption of how many elements I have in the array):
renderOtherInputs() {
return numOfIngredients.get().map((elem) => {
return(
<input type="text"/>
);
}
}
The idea is: when I click Add button then new element is pushed to the ReactiveVar (newNumOfIngredients). In the html code I call this.renderOtherInputs(), which return html for the as many inputs as elements I have in my ReactiveVar (newNumOfIngredients). newNumOfIngredients is a reactive storage of data - so when I push element to it, all things that depends on it should re-render. I have no idea why that is not working and how to do this.
Thank you for your help.
Finally I got the solution. But why you guys don't help newbie in web? It is really simple question for experienced developers. I read that meteor and especially react have powerful communities, but...
the answer is: we should use state!
first let's define our state object in the constructor of react component:
constructor(props) {
super(props);
this.state = {
inputs: [],
}
}
then we need a function to render inputs underlying our state.inputs:
renderOtherInputs() {
return this.state.inputs.map( (each, index) => {
return (
<input key={ index } type="text" />
);
});
}
and to add an input:
addInput(e) {
e.preventDefault();
var temp = this.state.inputs;
temp.push('no matter');
this.setState({
inputs: temp,
});
}
p.s. and to delete each input:
deleteIngredient(e) {
e.preventDefault();
let index = e.target.getAttribute('id');
let temp = this.state.inputs;
delete temp[index];
this.setState({
inputs: temp,
});
}

Does React component definetely update itself when a method is called

Although you don t need to look at the code below to understand the question, I added it in case you need to visualize the scenario. Whenever the form submits, addList method is called.
And the component updates itself. But I didn t expect this behaviour, that s why at first I did try to assign my lists to state, so that when the state changed the component would update itself as I wanted.
Anyway it already updates itself, but why ? Which way is more efficient ?
import React,{Component} from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
import {Lists} from '../lib/collections/lists.js';
export default class App extends TrackerReact(Component) {
constructor(){
super();
// this.state = {lists : this.lists()}
}
lists(){
return Lists.find().fetch();
}
addList(e){
e.preventDefault();
//let text = this.refs.list.value ;
let text = this._inputList.value ;
console.log(this._inputList.value);
Lists.insert({
title : text
});
this._inputList.value = "";
//this.setState({lists : this.lists()});
}
render() {
return (
<div>
<h2>Lists</h2>
<ul>
{this.lists().map((a,b)=>(
<List key={a._id} title={a.title} />
))}
</ul>
<form onSubmit={this.addList.bind(this)}>
<input
type="text"
ref={(input)=>{
this._inputList = input ;
}}
placeholder="add list bro"
/>
</form>
</div>
)
}
}
So if we add a componentWillUpdate react life-cycle method to see if it rerenders ;
componentWillUpdate() {
console.log('will update');
}
When form submitted "will update" is logged on console as expected. However if we update addList as ;
addList(e){
e.preventDefault();
// nothing else.
}
We don t see the output "will update" on console. Which means method being called doesn t require the component to be rerendered. There is no such a rule. In this case , it is probably about TrackerReact. TrackerReact might force the component to rerender.

Resources