How can I set lineItem data payload in shopware6 using html inputs - shopware

i'm using some inputs to create a cart line item:
<input type="hidden" name="lineItems[{{ id }}][id]" value="{{ id }}">
<input type="hidden" name="lineItems[{{ id }}][type]" value="product">
<input type="hidden" name="lineItems[{{ id }}][quantity]" value="1">
how can I set the payload to this to use with getPayload() or getPayloadValue() in cart collector/processor because using
<input type="hidden" name="lineItems[{{ id }}][payload][key]" value="value">
or
<input type="hidden" name="lineItems[{{ id }}][payloadData]" value="value">
does not work

The payload of line items is not fed by request parameters by default.
One possible approach would be to decorate the CartItemAddRoute and inject RequestStack to set the payload from the request parameters. You'll need to create a plugin to do so.
Service definition:
<service id="My\Plugin\Decorator\CartItemAddRouteDecorator" decorates="Shopware\Core\Checkout\Cart\SalesChannel\CartItemAddRoute">
<argument type="service" id="My\Plugin\Decorator\CartItemAddRouteDecorator.inner"/>
<argument type="service" id="request_stack"/>
</service>
Decorator:
class CartItemAddRouteDecorator extends AbstractCartItemAddRoute
{
private AbstractCartItemAddRoute $decorated;
private RequestStack $requestStack;
public function __construct(
AbstractCartItemAddRoute $decorated,
RequestStack $requestStack
) {
$this->decorated = $decorated;
$this->requestStack = $requestStack;
}
public function getDecorated(): AbstractCartItemAddRoute
{
return $this->decorated;
}
/**
* #param array<LineItem>|null $items
*/
public function add(Request $request, Cart $cart, SalesChannelContext $context, ?array $items): CartResponse
{
$params = $this->requestStack->getCurrentRequest()->request->all();
if ($items) {
foreach ($items as $item) {
if (empty($params['lineItems'][$item->getReferencedId()]['payload']['myKey'])) {
continue;
}
$item->setPayloadValue(
'myKey',
$params['lineItems'][$item->getReferencedId()]['payload']['myKey']
);
}
}
return $this->getDecorated()->add($request, $cart, $context, $items);
}
}
Template:
<input type="text" name="lineItems[{{ product.id }}][payload][myKey]" />

Related

How to localize error messages in .NET Core 6

I am new to .NET Core 6 and I have realized that several things should be done manually.
One of them is localization. For example, when using a simple action to change the user password, the message "Incorrect password." is returned in the errors collection when the old password mismatch.
I have spent a lot of time trying to localize that simple message so that it is shown in Spanish. I have read a lot of pages telling about this but none works. I think it is because this message is not a DataAnnotation message.
When I used .NET Framework, all of these were made automatically since the resource DLL is always installed by default. It seems that in .NET Core 6 those DLL's are missing, or at least, they are vey hidden.
As an attempt, I added this to Program.cs file:
builder.Services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, options => options.ResourcesPath = "Resources")
.AddDataAnnotationsLocalization();
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("es") };
options.DefaultRequestCulture = new RequestCulture("es");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
app.UseRequestLocalization();
And also added a file "Resouces\ErrorMessages.es.resx" with an entry whose key is PasswordMismatch with the message in Spanish, but no avail.
Any help, please?
Alought I couldn't check the details of your resourcefile,I think there's something wrong with your resourcefile for the name you've shown
Resouces\ErrorMessages.es.resx
I tried in my case as below:
public class TestModel
{
[Required(ErrorMessage = "The Email field is required.")]
[EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
[Display(Name = "Email")]
public string Email { get; set; }
[Required(ErrorMessage = "The Password field is required.")]
[StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
in Razor View:
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
It works well:
Update:
Since You are not interested in developing a multilingual site,I think the most simlpe solution is Custom an Attribute as below:
public class CusRequiredAttribute: RequiredAttribute
{
public CusRequiredAttribute()
{
//You could wrote the errormessage in your language as well
ErrorMessage = "{0}Required";
}
}
add [CusRequired]attrbute on the target property
for developing a multilingual site
Create an empty class named SharedResources and a resource file as below:
set as below in startup:
services.AddControllersWithViews()
.AddDataAnnotationsLocalization(
options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResources));
});
Result:

How to use seach function by "int" in ASP.NET Core MVC?

I use this way add seach function.
But it only can search by string.
I want to add a function of seach by "int".
Anyone have suggest?
public async Task<IActionResult> Index(string searchString)
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(await movies.ToListAsync());
}
And it is my resource
https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/search?view=aspnetcore-5.0
Also I have try like this way,but it seem didn't work
Search By Number in ASP.NET MVC
To search value by Int parameter, it is similar like search by string. In the View page, set the element's name attribute as same as the parameter name in the action method.
You can check the following sample:
<form asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Find by name: <input type="text" name="searchString" value="#ViewData["CurrentFilter"]" />
Find by ID: <input type="text" name="searchId" value="#ViewData["CurrentID"]" />
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
Controller:
public async Task<IActionResult> Index(string searchString, int searchId)
{
var students = from stu in _context.Students
select stu;
//if (!String.IsNullOrEmpty(searchString))
//{
// students = students.Where(s => s.LastName.Contains(searchString.ToString()));
//}
if (searchId !=0)
{
students = students.Where(s => s.ID == searchId );
}
ViewData["CurrentFilter"] = searchString;
ViewData["CurrentID"] = searchId;
return View(await students.ToListAsync());
}
Then, the result as below:

mvc 5 custom validation for variables inside of model object

I have searched for this question in multiple places and was unable to find exactly what I am looking for. Let's say I have this MVC Model Structure:
public class Person {
[Required]
public string Name { get; set; }
public int Age { get; set; }
}
public class Workers {
[AgeRequired]
public Person Pilots { get; set; }
public Person Chefs { get; set; }
}
and here would be my cshtml code:
#Model Workers
<div>
<label asp-for="Pilots.Name"></label>
<input asp-for="Pilots.Name"></input>
<span asp-validation-for="Pilots.Name"></span>
</div>
<div>
<label asp-for="Pilots.Age"></label>
<input asp-for="Pilots.Age"></input>
<span asp-validation-for="Pilots.Age"></span>
</div>
<div>
<label asp-for="Chefs.Name"></label>
<input asp-for="Chefs.Name"></input>
<span asp-validation-for="Chefs.Name"></span>
</div>
<div>
<label asp-for="Chefs.Age"></label>
<input asp-for="Chefs.Age"></input>
<span asp-validation-for="Chefs.Age"></span>
</div>
Person is a generic Model class that holds information about Pilots or Chefs. What I want is for my AgeRequired Custom Validation Attribute to make Age required only when referring to Pilots, not Chefs. Would that be possible?
I have it working on the backend side, after the form has been submitted, however I would like this to be on the front end as well. Here is my code for my Attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class AgeRequiredAttribute: ValidationAttribute, IClientModelValidator
{
public override bool IsValid(object value)
{
Workers workers = value as Workers;
return workers.Age > 0;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
{
yield return new ModelClientValidationRule("agerequired", "{0} is a required field.");
}
}
}
and here is my javascript code for the front end validation:
/// <reference path="jquery.validate.js" />
/// <reference path="jquery.validate.unobtrusive.js" />
$.validator.addMethod("agerequired",
function (value, element, parameters) {
return value > 0;
});
$.validator.unobtrusive.adapters.add("agerequired", [], function (options) {
options.rules.agerequired= {};
options.messages["agerequired"] = options.message;
});
ClientValidationEnabled and UnobstrusiveJavaScriptEnabled are both set to true.
This custom attribute will work when I have it on the Age field itself, but that makes it required for both Pilots and Chefs. I only want it required for Pilots.
Thank you in advance for your help!
I was actually able to create a work around for those who are interested.
If you put in the data-val-[attribute] that would get generated by MVC directly into your input tag or select tag, followed by the error message you want to throw, it will do the front end validation, and still do the back end validation as MVC will notice that the complex object has information in it. It's not ideal, but probably what I will have to do.
For example:
#Model Workers
<div>
<label asp-for="Pilots.Name"></label>
<input asp-for="Pilots.Name"></input>
<span asp-validation-for="Pilots.Name"></span>
</div>
<div>
<label asp-for="Pilots.Age"></label>
<input asp-for="Pilots.Age" data-val-agerequired="Age is Required for Pilots."></input>
<span asp-validation-for="Pilots.Age"></span>
</div>
<div>
<label asp-for="Chefs.Name"></label>
<input asp-for="Chefs.Name"></input>
<span asp-validation-for="Chefs.Name"></span>
</div>
<div>
<label asp-for="Chefs.Age"></label>
<input asp-for="Chefs.Age"></input>
<span asp-validation-for="Chefs.Age"></span>
</div>
Will work. It's not ideal, but it allows us to keep the MVC Back End validation in the same spot.
Another option is to have person age as optional in a base class.
public int? Age { get; set; }
Inherit Chef and Pilot from Person. In pilot, make the age value non-optional - you'll probably want to make sure they have a minimal age too using Data Annotations
public new int Age { get; set; }
The model is now a list of Persons. The client should be able to work out which is optional and which is not

ASP.NET core persisting values between Get and Post error validation

I'm new to web development so I don't know a good way on how to persist data between requests.
This is my site so far:
The elephant title is being fetched from an API on the GET request, according to the titleId query parameter. When I press login, model validations are being run, for example that email and password must have been entered. However, when error page is returned, elephant text is empty since that value was not persisted. What are the best approaches to persist that value so that is still visible when POST error is returned? Does it has to be included in the POST data? I don't want to request the API again.
Code behind:
public class IndexModel : PageModel
{
private string apiTitle;
public string ApiTitle { get { return apiTitle; } set { apiTitle = value; } }
// Bind form values
[BindProperty]
public User user { get; set; }
public Task<IActionResult> OnGetAsync(string titleId)
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
return Page();
}
public async Task<IActionResult> OnPostLoginAsync()
{
if (!IsLoginFormValid())
{
// When this is returned, for example if no password was entered,
// elephant title goes missing since apiTitle is null?
return Page();
}
var user = await LoginEmailAsync();
return RedirectToPage("/Profile");
}
}
Html:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<link rel="stylesheet" href="css/index.css">
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<div class="login-page">
<span id="label_api_title">#Model.ApiTitle</span>
<div class="form">
<form class="login-form" method="post" asp-page-handler="Login">
<input type="text" placeholder="Email" asp-for="User.Email"/>
<span asp-validation-for="User.Email" class="text-danger"></span>
<input type="password" placeholder="Password" asp-for="User.Password1" />
<span asp-validation-for="User.Password1" class="text-danger"></span>
<button>login</button>
<p class="message">Not registered? Create an account</p>
</form>
</div>
</div>
<script src="js/index.js"></script>
Yes. What you see is the expected behavior. Remember, Http is stateless. You are making 2 separate http calls, one for the GET and one for POST. The second call has no idea what the first call did ( or even there was first call at all!)
If you want to have a way to read the ApiTitle property value in the Post call and return that to the view, you need to persist it somewhere so that it is available between http calls. But in your case, all you need is to include that in the form post and have the framework bind it for you.
In your case, you can simply use a public property (Which is settable and gettable) for this. No need to keep a private variable. Decorate your property with BindProperty attribute so the model binder will bind the data on this property.
public class CreateModel : PageModel
{
[BindProperty]
public string ApiTitle { get; set; }
//Your existing code goes here
}
Now inside your form tag, have an input hidden element for the ApiTitle. This way, when the form is submitted, the value of ApiTitle property will be send in the request data.
<form class="login-form" method="post" asp-page-handler="Login">
<input type="hidden" asp-for="ApiTitle"/>
<input type="text" placeholder="Email" asp-for="User.Username"/>
<!--Your other existing form elements -->
<button>login</button>
</form>
Now in your OnPostLoginAsync method, you can read the ApiTitle value if needed. When you return the Page (when validation fails), the UI will display the ApiTitle property value in your span element.
public async Task<IActionResult> OnPostLoginAsync()
{
var title = this.ApiTitle; // If you want to read this value
if (!ModelState.IsValid)
{
return Page();
}
return RedirectToPage("/Profile");
}
It could be that your not sending it inside your form in the razor and its only in the get request and not in the post request:
public Task<IActionResult> OnGetAsync(string titleId)//<----its here
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
return Page();
}
//-->need to pass title id in post
public async Task<IActionResult> OnPostLoginAsync(string titleId)
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
if (!IsLoginFormValid())
{
// When this is returned, for example if no password was entered,
// elephant title goes missing since apiTitle is null?
return Page();
}
var user = await LoginEmailAsync();
return RedirectToPage("/Profile");
}
and in your razor add the span in the form:
<span id="label_api_title">#Model.ApiTitle</span>
<div class="form">
<form class="login-form" method="post" asp-page-handler="Login">
<span id="label_api_title">#Model.ApiTitle</span>/*<--------here*/
<input type="text" placeholder="Email" asp-for="User.Email"/>
<span asp-validation-for="User.Email" class="text-danger"></span>
<input type="password" placeholder="Password" asp-for="User.Password1"/>
<span asp-validation-for="User.Password1" class="text-danger"></span>
<button>login</button>
<p class="message">Not registered? Create an account</p>
</form>

Redux Form : How to add function after validation success and before submit

I have a redux form that have a validation. When the validation returns with no errors, it will by default submit to the controller in the handleSubmit().
class SomeComponent extends Component {
constructor(props) {
super(props)
}
render() {
const { handleSubmit, fields: { field }, submitting, values } = this.props;
return (
<form onSubmit={handleSubmit(this.props.someActionForm)}>
<TextField {...field.input}
type="text"
label="FIELD"
floatingLabelText="FIELD"
errorText={field.touched? field.error: null}
/>
<div className="col-xs-12">
<FlatButton
label="ACTIVATE"
fullWidth={true}
style={styles.fullButton}
backgroundColor="#4FCDCC"
hoverColor="#59e5e3"
type="submit"
disabled={submitting}
/>
</div>
</form>
);
}
}
function validate(values) {
const errors = {};
if(!values.field) {
errors.field= "Field must not be empty !!!";
}
return errors;
}
SomeComponent = reduxForm({
form: "SomeComponent",
fields: ['field'],
validate
}, mapStateToProps, mapDispatchToProps )(SomeComponent);
How can I add a function, for example a loading element to be triggered when the validation is successful but before the form submits to the action (handleSubmit())?
Thank you in advance
You can add a class method like this:
class SomeComponent extends Component {
submit(formProps) {
// your logic
this.props.someActionForm(formProps);
};
render() {
const {handleSubmit, fields: {field}, submitting, values} = this.props;
return (
<form onSubmit={handleSubmit(this.submit)}>
<TextField {...field.input}
type="text"
label="FIELD"
floatingLabelText="FIELD"
errorText={field.touched ? field.error : null}
/>
<div className="col-xs-12">
<FlatButton
label="ACTIVATE"
fullWidth={true}
style={styles.fullButton}
backgroundColor="#4FCDCC"
hoverColor="#59e5e3"
type="submit"
disabled={submitting}
/>
</div>
</form>
);
}
}
Notice that you forgot return in render method

Resources