I have used this solution of rendering a view into a string successfully until I had to do the validation.
This is on the view:
#Html.TextBoxFor(m => m.OrderedQuantity, new { id="someid", name="somename", data_mini = "true", type = "number", #class = "orderedQuantity removeGroupsRequired" })
and what the result is:
<input class="orderedQuantity removeGroupsRequired" data-mini="true" data-val="true" data-val-number="The field Quantity: must be a number." data-val-required="The Quantity: field is required." id="someid" name="OrderedQuantity" type="number" value="0" />
and I need the name in order do make some rules for an unobtrusive validation:
var form = $("#mydiv form");
form.validate(
{
rules: {
somename: {
required: true
}
},
messages: {
somename:
{
required: "you must provide a quantity!"
}
}
}
);
You cannot set the name attribute when using the TextBox helper. That's by design. So don't try it. This helper is designed to generate markup with input field names matching your view model properties so that when the form is submitted the default model binder will be able to bind the corresponding value to the property on the view model:
#Html.TextBoxFor(
m => m.OrderedQuantity,
new {
id = "someid",
data_mini = "true",
type = "number",
#class = "orderedQuantity removeGroupsRequired"
}
)
The name of the input field is OrderQuantity, so that's what you should use for your validation rules:
form.validate({
rules: {
OrderedQuantity: {
required: true
}
},
messages: {
OrderedQuantity: {
required: "you must provide a quantity!"
}
}
});
If you want to be able to use custom name, different than what the correct name for the model binding would have been (OrderedQuantity), then you will have to either write a custom helper or hardcode the markup (both are totally non-recommended solutions).
Related
I see in the docs
<input
{...register("test1", {
validate: {
positive: v => parseInt(v) > 0,
lessThanTen: v => parseInt(v) < 10,
checkUrl: async () => await fetch(),
}
})}
/>
So here how can i show different messages for each validation.
like number is -5, then i show
"Number is not positive"
"Number less than 10"
HOw to access each error positive and lessThanTen
Per default RHF will only show one error per field, so if there are multiple you will you don't have to loop over them. You can just use the errors object provided by RHF and access the name of your field and then the message property.
If you need to have all errors to be shown simultaneously you can set the config criteriaMode, check the docs here for more info.
function App() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="test1">Test Field</label>
<input
{...register("test1", {
validate: {
positive: (v) => parseInt(v) > 0 || "Number is not positive",
lessThanTen: (v) => parseInt(v) < 10 || "Number less than 10",
checkUrl: async () => {
const result = await Promise.resolve(true);
return result || "result was false so show this message";
}
}
})}
/>
{errors.test1 && <p>{errors.test1.message}</p>}
<input type="submit" />
</form>
);
}
To apply multiple validations, you can build a custom hook as a resolver. A custom hook can easily integrate with yup/Joi/Superstruct as a validation method, and be used inside validation resolver.
You can find more doc and examples here:
https://react-hook-form.com/advanced-usage/
in the section:
Custom Hook with Resolver
I recommend you yup.
I have two components: App and Registration Form
The form has two inputs: Name and Last name
Looking at the App state in dev. tools I see length: undefined and name: "name entered". I'm not getting any errors but I'm missing the last name.
This is only happening in Rails. I tried the same code in a non rails environment and it works fine. I'm using this gem for React: gem 'react-rails', '~> 1.5.0' and running Rails 4.2.4
var App = React.createClass({
getInitialState : function(){
return {
registrations: {}
}
},
addRegistration : function(registration){
// create unique id
var timestamp = (new Date()).getTime();
// update state
this.state.registrations['registration-' + timestamp] = registration;
//set the state
this.setState({ registrations : this.state.registrations });
},
render : function(){
return (
<RegistrationForm addRegistration={this.addRegistration}/>
)
}
});
var RegistrationForm = React.createClass({
createRegistration : function(event){
// prevent default
event.preventDefault();
// take data from form and create object
var registration = {
name : this.refs.name.value,
lastname : this.refs.lastname.value
}
// Add registration to App Object
this.props.addRegistration(registration);
this.refs.registrationForm.reset();
//console.log(registration);
},
render : function(){
return (
<div className="col-sm-12">
<form action="" className="form" ref="registrationForm" onSubmit={this.createRegistration}>
<div className="form-group">
<label >Name</label>
<input className="form-control" ref="name"/>
</div>
<div className="form-group">
<label >Last Name</label>
<input className="form-control" ref="lastname"/>
</div>
<div>
<button className="btn btn-primary">Submit</button>
</div>
</form>
</div>
)
}
});
App = React.createFactory(App)
What I'm trying to do is to give each registration a unique id number based on the time stamp.
When I console log the following:
addRegistration : function(registration){
// create unique id
var timestamp = (new Date()).getTime();
// update state
this.state.registrations['registration-' + timestamp] = registration;
//set the state
this.setState({ registrations : this.state.registrations });
},
I can see a registration object the way I want it. I can add as many unique registrations to the App state but each registration has length: undefined, name: "name" , but it's missing the last name.
If I change the set state to this:
this.setState({ registrations : registration });
This gives me a single registration with name and last name but it doesn't add multiple registrations. It only creates one registration which gets update every time I submit the add registration form.
this.state.registrations['registration-' + timestamp] = registration;
You seem to be mutating the state directly, based on the React Docs https://facebook.github.io/react/docs/component-api.html
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but
creates a pending state transition. Accessing this.state after calling
this method can potentially return the existing value.
There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
setState() will always trigger a
re-render unless conditional rendering logic is implemented in
shouldComponentUpdate(). If mutable objects are being used and the
logic cannot be implemented in shouldComponentUpdate(), calling
setState() only when the new state differs from the previous state
will avoid unnecessary re-renders.
Try cloning the current state then use that as the argument.
// if array
var clonedRegistration = this.state.registrations.slice();
clonedRegistration['registration-' + timestamp] = registration;
this.setState({registrations: clonedRegistration})
or
this.setState({registrations: {['registration-'+ timestamp]: registration} });
I think the answer Road put was close.
First set your initial state to an array.
getInitialState: function(){
return { registrations: []}
}
your addRegistration function
addRegistration : function(registration){
I think this is what you're missing:
//getting current state
var oldRegistrations = this.state.registrations;
Otherwise I believe you're updating the same thing over and over, instead of adding a new registration object. Then push your registration. You should set the timestamp
// update state
oldRegistrations.push(registration);
var registrations = oldRegistrations;
//set the state
this.setState({ registrations : registrations });
},
I would advise creating the id somewhere in here since you're not using an actual ajax call to a rails db:
var registration = {
name : this.refs.name.value,
lastname : this.refs.lastname.value
id: (new Date()).getTime();
}
I'm not sure I understand your question regarding your form values or if you were having trouble with them. But if you were I think doing something like this may help:
<input type='text' className='form-control'
placeholder='Name' name='name'
value={this.state.name} onChange={this.handleChange} >
</input>
<input type='text' className='form-control'
placeholder='Last Name' name='last name'
value={this.state.last_name} onChange={this.handleChange} >
</input>
Then implement a handleChange function within the same component to constantly handle the form's values onChange. That should look like this:
handleChange: function(e) {
var name = e.target.name;
var obj = {};
obj[name] = e.target.value;
this.setState(obj);
}
Hope this helps,
I am trying to present a form to my user where they can enter a start postcode, hit a button and end up on a page offering driving directions to a known postcode.
The route I want to match is defined as:
routes.MapRoute("Directions", "Directions/{Name}/{StartPostCode}-to-{DestinationPostCode}", new { controller = "Directions", action = "Index" });
I am using the following code to present the form:
#using (#Html.BeginForm("Index", "Directions", new { DestinationPostCode = Model.postcode, Name = Model.nameURLized }, FormMethod.Get))
{
<input type="text" name="StartPostCode" id="StartPostCode" class="inputGreyed" value="Enter Postcode" onclick="ToggleDefaultText(this)"/>
<input type="submit" value="Get Directions" />
}
The problem is that I end up at /Directions/Index?StartPostCode=abc123. It is missing the destination postcode and the name key value pairs. This of course means I end up with my request being processed by the wrong route. I have proved this using Phil Haacks route debugger.
I have tested going directly to /Directions/TheName/ABC123-to-DT83PX which works as expected. In fact, I tried this using the following code to build a link:
#Html.ActionLink("Directions Generated", "Index", "Directions", new { StartPostCode = "ABC123", DestinationPostCode = Model.postcode, Name = Model.nameURLized }, new { #class = "button", #title = "More details on " + Model.name })
Any help will be much appreciated.
Please do the following:
a) add the default route:
routes.MapRoute(null, "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }
b) Pass the values for DestinationPostCode and ListingName in hidden input fields.
c) Use:
#using (#Html.BeginForm("Index", "Directions")) { your code }
I have the following macro:
<macro name="InputField" id="string" value="string">
...
<input type="text" id="${id}" name="${id}" value="${value} />
...
</macro>
And the call to the macro:
${InputField( "model.address.address1", 75, "Address", model.Address.Address1 )}
The only problem is that model.Address will be null in some situations
(creating the item instead of editing it), because of this the macro
doesn't run or fails and just outputs the macro call to the view.
How can I pass either "" or the value of model.Address.Address1
depending if Address is null or not? the null operator ($!{}) doesnt
seem to work in this instance.
Solution 1. Write method
public static string HandleNull(Func<object> func)
{
try { return func().ToString(); }
catch (NullReferenceException) { return ""; }
}
and use it instead of Spark macro.
${InputField( "model.address.address1", 75, "Address", HandleNull(() => model.Address.Address1) )}
Solution 2. Use http://www.jmill.net/taxonomy/term/312
Solution 3.
<macro name="InputField" id="string" value="Func<string>">
...
<input type="text" id="${id}" name="${id}" value="$!{value()} />
...
</macro>
${InputField( "model.address.address1", 75, "Address", () => model.Address.Address1 )}
All the solutions depend on deferred execution.
Currently when I want to set html attributes like maxlength and autocomplete, I have to use the following syntax:
<%= Html.TextBox("username", ViewData["username"], new { maxlength = 20, autocomplete = "off" }) %>
Is there any way to do this without having to explicitly set the ViewData["username"] portion? In other words, I want to rely on the helper method's automatic loading routine rather than having to explicitly tell it which field to load up from the ViewData.
Just pass "null" as second parameter:
<%= Html.TextBox("username", null, new { maxlength = 20, autocomplete = "off" }) %>
yes but you have to use ViewData.Model instead of ViewData.Item()
the code in your controller should look like this (sry 4 VB.NET code)
Function Index()
ViewData("Title") = "Home Page"
ViewData("Message") = "Welcome to ASP.NET MVC!"
Dim user As New User
Return View(user)
End Function
now you can do this in the view
<%=Html.TextBox("username", Nothing, New With {.maxlength = 30})%>
note that the user object has a public property username
hth
I used construction as below:
<%= Html.TextBox("username", "", new { #maxlength = "20", #autocomplete = "off" }) %>
For Setting max length of TextBox you can pass "" or null for Second Parameter and set html attributes(maxlength) as third parameter
<%=Html.TextBox("username", "", new { #maxlength = 10 }) %>