Extending Dynamic-forms from angular2 cookbook to be able to have arrays - forms

I have tried to extend the Dynamic-Forms app from angular 2 cookbook so I can have an array of multiple fields. I have been able to show the fields, but when I'm filling the form, I'm getting just the last element of the array filled.
Hier is my plunker with that state of the app:
//question-array.ts
import { QuestionBase } from './question-base';
export class ArrayQuestion extends QuestionBase<string> {
controlType = 'array';
options:{childe:FormElement<string>}[] = [];
constructor(options:{} = {}){
super(options);
this.options = options['options'] || [];
}
}
//question-control.service.ts
...
toFormGroup(formelements: QuestionBase<any>[] ) {
let group:any = {};
formelements.forEach(element => {
console.log("+element",element);
if(element.controlType === "array"){
let arr:any[] = [];
let locobj = {};
element["options"].forEach((option:any) => {
option["element"].forEach((e:any) =>{
locobj[e.key] = new FormControl(e.value || '');
});
arr.push(new FormGroup(locobj))
});
group[element.key] = new FormArray(arr);
}else{
group[element.key] = element.required ? new FormControl(element.value || '', Validators.required)
: new FormControl(element.value || '');
}
});
return new FormGroup(group);
}
//dynamic-form-question.component.html
...
<div *ngSwitchCase="'array'" [formArrayName]="question.key">
<div *ngFor="let item of question.options; let i=index" [formGroupName]="i" >
<div *ngFor="let element of item.element">
<div *ngIf="element.controlType === 'textbox'" >
<label>{{element.label}}</label>
<input [formControlName]="element.key" [id]="element.key" [type]="element.type" />
</div>
<div *ngIf="element.controlType === 'dropdown'" >
<label>{{item.label}}</label>
<select [id]="element.key" [formControlName]="element.key">
<option *ngFor="let opt of element.options" [value]="opt.key">{{opt.value}}</option>
</select>
</div>
</div>
</div>
</div>
...
http://plnkr.co/edit/DZ05fO
Best regard,
Shefki

I found the bug, the problem was that I was passing the same reference in the form group her is what i changed:
//question-control.service.ts
toFormGroup(formelements: QuestionBase[] ) {
let group:any = {};
formelements.forEach(element => {
if(element.controlType === "array"){
let arr:any[] = [];
let locobj = {};
element["options"].forEach((option:any) => {
option["element"].forEach((e:any) =>{
locobj[e.key] = e.value || '';
});
arr.push(new FormGroup(this.getFormControlObject(locobj)));
});
group[element.key] = new FormArray(arr);
}else{
group[element.key] = element.required ? new FormControl(element.value || '', Validators.required)
: new FormControl(element.value || '');
}
});
return new FormGroup(group);
}
private getFormControlObject(keys){
let retobj = {};
Object.keys(keys).forEach(function(key) {
retobj[key] = new FormControl(keys[key]);
});
return retobj;
}
Hier is a working plunker
http://plnkr.co/edit/4IMKdLKE51n41jzYY8sU

Related

MVC app does not show selected values on a form reedition

I am building a Framework7 MVC app and found myself in a dead end alley. I have a form which I need to evaluate. This form contains selects. I am using localStorage to store the form values and everything works OK in that sense, I mean everything is stored correctly. ¿What is the issue? When I fill the form I answer some questions on textareas inputs, select inputs and inputs. everything goes fine until I try to reedit the form, then everything is display correctly on the form, including the score i got from my previous answers, but, the selects appears as if I have never touch them. Their previously selected value is stored but not display on the form. I have found that the issue is caused by the fact that I have set numerical values to the options values but what the form show is "yes" or "no". If I change the option values to "yes" or "no" then the form displays correctly but I need to set "5" or "0" because I need to evaluate the user's answers.
This is my code
The form
<li style="margin-top:-10px;">
<input style="visibility:hidden;height:1px;" value="0" name="choice" onchange="checkTotal()"/>
<input style="visibility:hidden;height:1px;" value="1" type="checkbox" name="choice" onchange="checkTotal()" checked="on">
</li>
<li><div class="item-content">1. ¿Sueles quejarte de sentirte mal?</div>
<div class="item-content">
<div class="item-inner">
<div class="item-input">
<select name="pr1" id="pr1" onchange="checkTotal()">
<option class="item-inner" value="5">No</option>
<option class="item-inner" value="0">Si</option>
</select>
</div>
</div>
</div>
<div class="item-content">En tal caso,</div>
<div class="item-content">
<div class="item-inner">
<div class="item-input">
<textarea class="resizable" id="pr1notes" placeholder="¿cuál es la causa?">{{model.pr1notes}}</textarea>
</div>
</div>
</div>
</li>
The functions on the editController
function init(query){
var protections = JSON.parse(localStorage.getItem("f7Protections"));
if (query && query.id) {
protection = new Protection(_.find(protections, { id: query.id }));
state.isNew = false;
}
else {
protection = new Protection({ isFavorite: query.isFavorite });
state.isNew = true;
}
View.render({ model: protection, bindings: bindings, state: state, doneCallback: saveProtection });
showSelectedValues();
}
function showSelectedValues(){
var fieldNames = protection.getSelectFields();
for (var i = 0, len = fieldNames.length; i < len; i++) {
var itemname = fieldNames[i];
var selectObj = document.getElementById(itemname);
if (selectObj!=null) {
var objOptions = selectObj.options;
var selIndex=0;
for (var j = 0, len2 = objOptions.length; j < len2; j++) {
if ((objOptions[j].label).localeCompare(protection[itemname])==0){
selIndex=j;
}
}
selectObj.options[selIndex].setAttribute("selected","selected");
}else{
}
}
}
and the model
Protection.prototype.setValues = function(inputValues, extras) {
for (var i = 0, len = inputValues.length; i < len; i++) {
var item = inputValues[i];
if (item.type === 'checkbox') {
this[item.id] = item.checked;
}
else {
this[item.id] = item.value;
}
}
for (var i = 0, len = extras[0].length; i < len; i++) {
var item = extras[0][i];
if((item.id).localeCompare("pr1notes")==0) {this[item.id] = item.value;}
}
console.log('starting loop for extras 3...');
for (var i = 0, len = extras[2].length; i < len; i++) {
var item = extras[2][i];
this[item.name] = item.value;
}
};
Protection.prototype.validate = function() {
var result = true;
if (_.isEmpty(this.prdate)
) {result = false;}
return result;
};
Protection.prototype.getSelectFields = function() {
return ['pr1'];
}
What should I change in order to keep my "5" or "0" values on the select options while the form options still show "yes" or "no" to the user just like this: <select name="pr1" id="pr1" onchange="checkTotal()"><option class="item-inner" value="5">No</option><option class="item-inner" value="0">Si</option></select>?
need anything else to help you understand the issue?
The simplest solution
function init(query){
var protections = JSON.parse(localStorage.getItem("f7Protections"));
if (query && query.id) {
protection = new Protection(_.find(protections, { id: query.id }));
state.isNew = false;
}
else {
protection = new Protection({ isFavorite: query.isFavorite });
state.isNew = true;
}
View.render({ model: protection, bindings: bindings, state: state, doneCallback: saveProtection });
showSelectedValues();
}
function showSelectedValues(){
var fieldNames = protection.getSelectFields();
for (var i = 0, len = fieldNames.length; i < len; i++) {
var itemname = fieldNames[i];
var selectObj = document.getElementById(itemname);
if (selectObj!=null) {
var objOptions = selectObj.options;
var selIndex=0;
for (var j = 0, len2 = objOptions.length; j < len2; j++) {
if ((objOptions[j].value).localeCompare(protection[itemname])==0){
selIndex=j;
}
}
selectObj.options[selIndex].setAttribute("selected","selected");
}else{
}
}
}
Just changed this line
if ((objOptions[j].label).localeCompare(protection[itemname])==0){
selIndex=j;
and changed .label for .value.

How can I had multiple chekbox on account creation on Prestashop 1.6

I would like to know more about my customers during their registration on my website. For this I just want to add on the account creation 5 checkoxes (he can choose more than one checkbox) and save it in the database and in personnal information in the account. Easy for me to add the checkboxes in the form, but I have no idea to do the rest.
I already add the checkboxes in the code, but I don't know what I need to change after. I modified only the authentication.tpl file.
This is my code to add the checkboxes in the form :
<p class="textarea">
<label for="other">{l s='Je suis :'}</label>
<div id="other_info">
<input class="checkboxx" type="checkbox" value="{if isset($smarty.post.other)}{$smarty.post.other}{/if}" id="other" name="Céréalier">
<span class="cerealier">Céréalier</span>
<input class="checkboxx" type="checkbox" value="{if isset($smarty.post.other)}{$smarty.post.other}{/if}" id="other" name="Éleveur">
<span class="eleveur">Éleveur</span>
<input class="checkboxx" type="checkbox" value="{if isset($smarty.post.other)}{$smarty.post.other}{/if}" id="other" name="Viticulteur">
<span class="viticulteur">Viticulteur</span>
<input class="checkboxx" type="checkbox" value="{if isset($smarty.post.other)}{$smarty.post.other}{/if}" id="other" name="Revendeur">
<span class="revendeur">Revendeur</span>
<input class="checkboxx" type="checkbox" value="{if isset($smarty.post.other)}{$smarty.post.other}{/if}" id="other" name="Autre">
<span class="autre">Autre</span><br>
</div>
</p>
I would like someone to help to realise this implemation on account creation with my multiple checkboxes.
This is my method processSubmitAccount() :
protected function processSubmitAccount()
{
Hook::exec('actionBeforeSubmitAccount');
$this->create_account = true;
if (Tools::isSubmit('submitAccount')) {
$this->context->smarty->assign('email_create', 1);
}
// New Guest customer
if (!Tools::getValue('is_new_customer', 1) && !Configuration::get('PS_GUEST_CHECKOUT_ENABLED')) {
$this->errors[] = Tools::displayError('You cannot create a guest account.');
}
if (!Tools::getValue('is_new_customer', 1)) {
$_POST['passwd'] = md5(time()._COOKIE_KEY_);
}
if ($guest_email = Tools::getValue('guest_email')) {
$_POST['email'] = $guest_email;
}
// Checked the user address in case he changed his email address
if (Validate::isEmail($email = Tools::getValue('email')) && !empty($email)) {
if (Customer::customerExists($email)) {
$this->errors[] = Tools::displayError('An account using this email address has already been registered.', false);
}
}
// Preparing customer
$customer = new Customer();
$other = Tools::getValue('other');
var_dump($other);
exit;
$customer->other = $other;
$lastnameAddress = Tools::getValue('lastname');
$firstnameAddress = Tools::getValue('firstname');
$_POST['lastname'] = Tools::getValue('customer_lastname', $lastnameAddress);
$_POST['firstname'] = Tools::getValue('customer_firstname', $firstnameAddress);
$addresses_types = array('address');
if (!Configuration::get('PS_ORDER_PROCESS_TYPE') && Configuration::get('PS_GUEST_CHECKOUT_ENABLED') && Tools::getValue('invoice_address')) {
$addresses_types[] = 'address_invoice';
}
$error_phone = false;
if (Configuration::get('PS_ONE_PHONE_AT_LEAST')) {
if (Tools::isSubmit('submitGuestAccount') || !Tools::getValue('is_new_customer')) {
if (!Tools::getValue('phone') && !Tools::getValue('phone_mobile')) {
$error_phone = true;
}
} elseif (((Configuration::get('PS_REGISTRATION_PROCESS_TYPE') && Configuration::get('PS_ORDER_PROCESS_TYPE'))
|| (Configuration::get('PS_ORDER_PROCESS_TYPE') && !Tools::getValue('email_create'))
|| (Configuration::get('PS_REGISTRATION_PROCESS_TYPE') && Tools::getValue('email_create')))
&& (!Tools::getValue('phone') && !Tools::getValue('phone_mobile'))) {
$error_phone = true;
}
}
if ($error_phone) {
$this->errors[] = Tools::displayError('You must register at least one phone number.');
}
$this->errors = array_unique(array_merge($this->errors, $customer->validateController()));
// Check the requires fields which are settings in the BO
$this->errors = $this->errors + $customer->validateFieldsRequiredDatabase();
if (!Configuration::get('PS_REGISTRATION_PROCESS_TYPE') && !$this->ajax && !Tools::isSubmit('submitGuestAccount')) {
if (!count($this->errors)) {
$this->processCustomerNewsletter($customer);
$customer->firstname = Tools::ucwords($customer->firstname);
$customer->birthday = (empty($_POST['years']) ? '' : (int)Tools::getValue('years').'-'.(int)Tools::getValue('months').'-'.(int)Tools::getValue('days'));
if (!Validate::isBirthDate($customer->birthday)) {
$this->errors[] = Tools::displayError('Invalid date of birth.');
}
// New Guest customer
$customer->is_guest = (Tools::isSubmit('is_new_customer') ? !Tools::getValue('is_new_customer', 1) : 0);
$customer->active = 1;
if (!count($this->errors)) {
if ($customer->add()) {
if (!$customer->is_guest) {
if (!$this->sendConfirmationMail($customer)) {
$this->errors[] = Tools::displayError('The email cannot be sent.');
}
}
$this->updateContext($customer);
$this->context->cart->update();
Hook::exec('actionCustomerAccountAdd', array(
'_POST' => $_POST,
'newCustomer' => $customer
));
if ($this->ajax) {
$return = array(
'hasError' => !empty($this->errors),
'errors' => $this->errors,
'isSaved' => true,
'id_customer' => (int)$this->context->cookie->id_customer,
'id_address_delivery' => $this->context->cart->id_address_delivery,
'id_address_invoice' => $this->context->cart->id_address_invoice,
'token' => Tools::getToken(false)
);
$this->ajaxDie(Tools::jsonEncode($return));
}
if (($back = Tools::getValue('back')) && $back == Tools::secureReferrer($back)) {
Tools::redirect(html_entity_decode($back));
}
// redirection: if cart is not empty : redirection to the cart
if (count($this->context->cart->getProducts(true)) > 0) {
$multi = (int)Tools::getValue('multi-shipping');
Tools::redirect('index.php?controller=order'.($multi ? '&multi-shipping='.$multi : ''));
}
// else : redirection to the account
else {
Tools::redirect('index.php?controller='.(($this->authRedirection !== false) ? urlencode($this->authRedirection) : 'my-account'));
}
} else {
$this->errors[] = Tools::displayError('An error occurred while creating your account.');
}
}
}
} else {
// if registration type is in one step, we save the address
$_POST['lastname'] = $lastnameAddress;
$_POST['firstname'] = $firstnameAddress;
$post_back = $_POST;
// Preparing addresses
foreach ($addresses_types as $addresses_type) {
$$addresses_type = new Address();
$$addresses_type->id_customer = 1;
if ($addresses_type == 'address_invoice') {
foreach ($_POST as $key => &$post) {
if ($tmp = Tools::getValue($key.'_invoice')) {
$post = $tmp;
}
}
}
$this->errors = array_unique(array_merge($this->errors, $$addresses_type->validateController()));
if ($addresses_type == 'address_invoice') {
$_POST = $post_back;
}
if (!($country = new Country($$addresses_type->id_country)) || !Validate::isLoadedObject($country)) {
$this->errors[] = Tools::displayError('Country cannot be loaded with address->id_country');
}
if (!$country->active) {
$this->errors[] = Tools::displayError('This country is not active.');
}
$postcode = $$addresses_type->postcode;
/* Check zip code format */
if ($country->zip_code_format && !$country->checkZipCode($postcode)) {
$this->errors[] = sprintf(Tools::displayError('The Zip/Postal code you\'ve entered is invalid. It must follow this format: %s'), str_replace('C', $country->iso_code, str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format))));
} elseif (empty($postcode) && $country->need_zip_code) {
$this->errors[] = Tools::displayError('A Zip / Postal code is required.');
} elseif ($postcode && !Validate::isPostCode($postcode)) {
$this->errors[] = Tools::displayError('The Zip / Postal code is invalid.');
}
if ($country->need_identification_number && (!Tools::getValue('dni') || !Validate::isDniLite(Tools::getValue('dni')))) {
$this->errors[] = Tools::displayError('The identification number is incorrect or has already been used.');
} elseif (!$country->need_identification_number) {
$$addresses_type->dni = null;
}
if (Tools::isSubmit('submitAccount') || Tools::isSubmit('submitGuestAccount')) {
if (!($country = new Country($$addresses_type->id_country, Configuration::get('PS_LANG_DEFAULT'))) || !Validate::isLoadedObject($country)) {
$this->errors[] = Tools::displayError('Country is invalid');
}
}
$contains_state = isset($country) && is_object($country) ? (int)$country->contains_states: 0;
$id_state = isset($$addresses_type) && is_object($$addresses_type) ? (int)$$addresses_type->id_state: 0;
if ((Tools::isSubmit('submitAccount') || Tools::isSubmit('submitGuestAccount')) && $contains_state && !$id_state) {
$this->errors[] = Tools::displayError('This country requires you to choose a State.');
}
}
}
if (!#checkdate(Tools::getValue('months'), Tools::getValue('days'), Tools::getValue('years')) && !(Tools::getValue('months') == '' && Tools::getValue('days') == '' && Tools::getValue('years') == '')) {
$this->errors[] = Tools::displayError('Invalid date of birth');
}
if (!count($this->errors)) {
if (Customer::customerExists(Tools::getValue('email'))) {
$this->errors[] = Tools::displayError('An account using this email address has already been registered. Please enter a valid password or request a new one. ', false);
}
$this->processCustomerNewsletter($customer);
$customer->birthday = (empty($_POST['years']) ? '' : (int)Tools::getValue('years').'-'.(int)Tools::getValue('months').'-'.(int)Tools::getValue('days'));
if (!Validate::isBirthDate($customer->birthday)) {
$this->errors[] = Tools::displayError('Invalid date of birth');
}
if (!count($this->errors)) {
$customer->active = 1;
// New Guest customer
if (Tools::isSubmit('is_new_customer')) {
$customer->is_guest = !Tools::getValue('is_new_customer', 1);
} else {
$customer->is_guest = 0;
}
if (!$customer->add()) {
$this->errors[] = Tools::displayError('An error occurred while creating your account.');
} else {
foreach ($addresses_types as $addresses_type) {
$$addresses_type->id_customer = (int)$customer->id;
if ($addresses_type == 'address_invoice') {
foreach ($_POST as $key => &$post) {
if ($tmp = Tools::getValue($key.'_invoice')) {
$post = $tmp;
}
}
}
$this->errors = array_unique(array_merge($this->errors, $$addresses_type->validateController()));
if ($addresses_type == 'address_invoice') {
$_POST = $post_back;
}
if (!count($this->errors) && (Configuration::get('PS_REGISTRATION_PROCESS_TYPE') || $this->ajax || Tools::isSubmit('submitGuestAccount')) && !$$addresses_type->add()) {
$this->errors[] = Tools::displayError('An error occurred while creating your address.');
}
}
if (!count($this->errors)) {
if (!$customer->is_guest) {
$this->context->customer = $customer;
$customer->cleanGroups();
// we add the guest customer in the default customer group
$customer->addGroups(array((int)Configuration::get('PS_CUSTOMER_GROUP')));
if (!$this->sendConfirmationMail($customer)) {
$this->errors[] = Tools::displayError('The email cannot be sent.');
}
} else {
$customer->cleanGroups();
// we add the guest customer in the guest customer group
$customer->addGroups(array((int)Configuration::get('PS_GUEST_GROUP')));
}
$this->updateContext($customer);
$this->context->cart->id_address_delivery = (int)Address::getFirstCustomerAddressId((int)$customer->id);
$this->context->cart->id_address_invoice = (int)Address::getFirstCustomerAddressId((int)$customer->id);
if (isset($address_invoice) && Validate::isLoadedObject($address_invoice)) {
$this->context->cart->id_address_invoice = (int)$address_invoice->id;
}
if ($this->ajax && Configuration::get('PS_ORDER_PROCESS_TYPE')) {
$delivery_option = array((int)$this->context->cart->id_address_delivery => (int)$this->context->cart->id_carrier.',');
$this->context->cart->setDeliveryOption($delivery_option);
}
// If a logged guest logs in as a customer, the cart secure key was already set and needs to be updated
$this->context->cart->update();
// Avoid articles without delivery address on the cart
$this->context->cart->autosetProductAddress();
Hook::exec('actionCustomerAccountAdd', array(
'_POST' => $_POST,
'newCustomer' => $customer
));
if ($this->ajax) {
$return = array(
'hasError' => !empty($this->errors),
'errors' => $this->errors,
'isSaved' => true,
'id_customer' => (int)$this->context->cookie->id_customer,
'id_address_delivery' => $this->context->cart->id_address_delivery,
'id_address_invoice' => $this->context->cart->id_address_invoice,
'token' => Tools::getToken(false)
);
$this->ajaxDie(Tools::jsonEncode($return));
}
// if registration type is in two steps, we redirect to register address
if (!Configuration::get('PS_REGISTRATION_PROCESS_TYPE') && !$this->ajax && !Tools::isSubmit('submitGuestAccount')) {
Tools::redirect('index.php?controller=address');
}
if (($back = Tools::getValue('back')) && $back == Tools::secureReferrer($back)) {
Tools::redirect(html_entity_decode($back));
}
// redirection: if cart is not empty : redirection to the cart
if (count($this->context->cart->getProducts(true)) > 0) {
Tools::redirect('index.php?controller=order'.($multi = (int)Tools::getValue('multi-shipping') ? '&multi-shipping='.$multi : ''));
}
// else : redirection to the account
else {
Tools::redirect('index.php?controller='.(($this->authRedirection !== false) ? urlencode($this->authRedirection) : 'my-account'));
}
}
}
}
}
if (count($this->errors)) {
//for retro compatibility to display guest account creation form on authentication page
if (Tools::getValue('submitGuestAccount')) {
$_GET['display_guest_checkout'] = 1;
}
if (!Tools::getValue('is_new_customer')) {
unset($_POST['passwd']);
}
if ($this->ajax) {
$return = array(
'hasError' => !empty($this->errors),
'errors' => $this->errors,
'isSaved' => false,
'id_customer' => 0
);
$this->ajaxDie(Tools::jsonEncode($return));
}
$this->context->smarty->assign('account_error', $this->errors);
}
}
First, you need to know that checkbox need same name but different values. so i think you more need radio buttons than checkboxes.
anyway, to add field to registration form, you need just some modifications (backup bdd and code first) :
modify HTML
modify Database structure
modify PHP
1) Template
<p class="textarea">
<label for="typeagri">{l s='Je suis :'}</label>
<div id="typeagri_info">
<span class="cerealier">Céréalier</span>
<input class="checkboxx" type="checkbox" value="Céréalier" id="typeagri1" name="typeagri[]">
<span class="eleveur">Éleveur</span>
<input class="checkboxx" type="checkbox" value="Éleveur" id="typeagri2" name="typeagri[]">
<span class="viticulteur">Viticulteur</span>
<input class="checkboxx" type="checkbox" value="Viticulteur" id="typeagri3" name="typeagri[]">
<span class="revendeur">Revendeur</span>
<input class="checkboxx" type="checkbox" value="Revendeur" id="typeagri4" name="typeagri[]">
<span class="autre">Autre</span>
<input class="checkboxx" type="checkbox" value="Autre" id="typeagri5" name="typeagri[]">
</div>
</p>
2) Database change (execute SQL in phpmyadmin)
ALTER TABLE ps_customer
ADD COLUMN typeagri VARCHAR(250)
3) PHP changes
in /controllers/front/AuthController.php
find "// if registration type is in one step, we save the address"
and after add that code :
$typeagris ='';
foreach(Tools::getValue('typeagri') as $t) {
$typeagris .= $t.',';
}
$customer->typeagri = $typeagris;
in /classes/Customer.php
find "public $firstname;" and after add that code :
/** #var string typeagri */
public $typeagri;
find "'passwd' => array('type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'required' => true, 'size' => 32)," and after add that code :
'typeagri' => array('type' => self::TYPE_STRING),
empty cache and test your modifications.
And for the identity form :
in indentity.tpl
After :
<div class="form-group">
<label for="website">{l s='Website'}</label>
<input type="text" class="form-control" id="website" name="website" value="{if isset($smarty.post.website)}{$smarty.post.website}{/if}" />
</div>
add :
<div class="form-group">
<p class="textarea">
<label for="typeagri">{l s='Je suis :'}</label>
<!--
{assign var="typeagris" value=","|explode:$smarty.post.typeagri}
{section name=i loop=$typeagris}
{$typeagris[i]}<br>
{/section}
-->
<div id="typeagri_info">
<input {if $smarty.post.typeagri|stristr:"alier"} checked=checked{/if} class="checkboxx" type="checkbox" value="Céréalier" id="typeagri1" name="typeagri[]"> <span class="cerealier">Céréalier</span>
<input {if $smarty.post.typeagri|stristr:"leveur"} checked=checked{/if} class="checkboxx" type="checkbox" value="Éleveur" id="typeagri2" name="typeagri[]"> <span class="eleveur">Éleveur</span>
<input {if $smarty.post.typeagri|stristr:"iticulteur"} checked=checked{/if} class="checkboxx" type="checkbox" value="Viticulteur" id="typeagri3" name="typeagri[]"> <span class="viticulteur">Viticulteur</span>
<input {if $smarty.post.typeagri|stristr:"evendeur"} checked=checked{/if} class="checkboxx" type="checkbox" value="Revendeur" id="typeagri4" name="typeagri[]"> <span class="revendeur">Revendeur</span>
<input {if $smarty.post.typeagri|stristr:"utre"}checked=checked{/if} class="checkboxx" type="checkbox" value="Autre" id="typeagri5" name="typeagri[]"> <span class="autre">Autre</span>
</div>
</p>
</div>
in IdentityController.php :
Before :
return $this->customer;
Add :
$typeagris ='';
foreach(Tools::getValue('typeagri') as $t) {
$typeagris .= $t.',';
}
$customer->typeagri = $typeagris;
If it works, you can now do the same with override (please take a look at http://doc.prestashop.com/pages/viewpage.action?pageId=51184698 )
Overriding is very important, to not mix native source code and your modifications.
Good luck

Angular 5 Angular Material 2 - autocomplete with minLength

I added autocomplete field on my form in order to select a patient.
The patient data come from database. The problem is there are 40.000 patients
So i would like to load data after user has entered 3 characters minimum.
But i don't know how to check that and how to pass the input to the function (filter argument).
This is what i have done. for the moment the data are loaded when i click on the input field :
HTML :
<mat-form-field class="index-full-width">
<input
matInput
type="text"
[(ngModel)]="patientChoice"
placeholder="Patient"
aria-label="Patient"
[matAutocomplete]="autoPatient"
[formControl]="myControl"
(click)="getPatients()">
<mat-autocomplete (optionSelected)="selectPat()" #autoPatient="matAutocomplete" [displayWith]="displayFnPat">
<mat-option *ngFor="let patient of filteredPatients | async" [value]="patient">
<span>{{ patient.lastName }}</span>
<small>{{patient.firstName}}</small> |
<span>né(e) le {{ patient.dateNaissance }}</span> |
<small>IPP: {{patient.ipp}}</small>
</mat-option>
</mat-autocomplete>
</mat-form-field>
TS :
getPatients() {
let searchTerm = '*';
let success: any = {};
this.klinckServices.getPatients(searchTerm)
.then((webScriptdata) => {
success = webScriptdata;
this.listPatients = success.data.items ;
console.log(this.listPatients);
},
msg => {
alert(msg);
});
}
ngOnInit() {
this.filteredPatients = this.myControl.valueChanges.pipe(
startWith<string | Patient>(''),
map(patient => typeof patient === 'string' ? patient : patient.name),
map(name => name ? this.filterPatient(name) : this.listPatients.slice())
);
}
displayFnPat(patient: Patient): string | undefined {
return patient ? patient.name : undefined;
}
filterPatient(name: string) {
return this.listPatients.filter(patient =>
patient.name.toLowerCase().includes(name.toLowerCase()));
}
There's another way to do it and it is recommended here.
It's basically checking the length on your filter method.
filterPatient(name: string) {
if (name.length < 2) {
return [];
}
return this.listPatients.filter(patient =>
patient.name.toLowerCase().includes(name.toLowerCase()));
}
OK, solved by adding in HTML (keyup)="getPatients($event)" :
<input
matInput
type="text"
[(ngModel)]="patientChoice"
placeholder="Patient"
aria-label="Patient"
[matAutocomplete]="autoPatient"
[formControl]="myControl"
(keyup)="getPatients($event)"
>
And in TS file :
getPatients(event: any) {
let searchTerm = '';
searchTerm += event.target.value;
console.log(searchTerm);
if (searchTerm.length === 2) {
let success: any = {};
this.klinckServices.getPatients(searchTerm)
.then((webScriptdata) => {
success = webScriptdata;
this.listPatients = success.data.items;
console.log(this.listPatients);
},
msg => {
alert(msg);
});
}
}

upload image using javascript and xajax to MySql Blob

I want to upload images to database, but I dont know how to receive this file.
//html
<input type="text" id="txtName">
<input type="file" id="image" onchange="sendImage()">
//Javascript
function sendImage()
{
var name = $('#txtName').val();
var image = $('#image').val(); //is this the way to send the image?
xajax_SaveImage(name, image);
}
//xajax
//with string I dont have problems for receive, but how receive I the image?
function SaveImage($name, $image)
{
//How here I convert the image to binary for to save in Mysql
}
You can do this: base64 is the image data, upload that data to database
File.prototype.convertToBase64 = function(callback){
var reader = new FileReader();
reader.onload = function(e) {
callback(e.target.result)
};
reader.onerror = function(e) {
callback(null, e);
};
reader.readAsDataURL(this);
};
$("#image").on('change',function(){
var selectedFile = this.files[0];
if (!selectedFile.name.match(/.(jpg|jpeg|png|gif)$/i)) {
}
else {
selectedFile.convertToBase64(function(base64){
//base64 is the base64 value of the image, use this to send to database...
})
}
});
Code snippet example:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" id="image">
<script>
File.prototype.convertToBase64 = function(callback){
var reader = new FileReader();
reader.onload = function(e) {
callback(e.target.result)
};
reader.onerror = function(e) {
callback(null, e);
};
reader.readAsDataURL(this);
};
$("#image").on('change',function(){
var selectedFile = this.files[0];
if (!selectedFile.name.match(/.(jpg|jpeg|png|gif)$/i)) {
}
else {
selectedFile.convertToBase64(function(base64){
console.log(base64);
})
}
});
</script>
In your template (smarty)
<div id="imageplaceholder" class="left shadow"><p>Place<br />Image<br />here.<br />(drag&drop)</p></div>
<div id="image">
<div id="imageborder" class="left shadow">{if $persondata.imagesize gt 0}<img src="getimage.php?h={$smarty.session.verify}&id={$persondata.id}" />{/if}</div>
</div><div id="imagetools" class="left">
<div id="imagehelp" class="imagetool help radius3 shadow"></div>
<div id="imagenew" class="imagetool new radius3 shadow"></div>
<div id="imagezoomin" class="imagetool zoomin radius3 shadow"></div>
<div id="imagezoomout" class="imagetool zoomout radius3 shadow"></div>
<div id="imageok" class="imagetool ok radius3 shadow"></div>
</div>
js part uses filedrop.
FileDrop JavaScript classes | by Proger_XP https://github.com/ProgerXP/FileDrop
$('#imageplaceholder').filedrop({
// The name of the $_FILES entry:
paramname:'pic',
url: '/img_file.php', //this is the PHP file used for processing
allowedfiletypes: ['image/jpeg','image/png','image/gif'],
uploadFinished:function(i,file,response){
// remove placeholder
// add Image
// $.data(file).addClass('done');
// response is the JSON object that img_file.php returns
$('#imageplaceholder').hide();
loadImage(file);
$('#image').show();
$('#imagetools .help').show();
$('#imagetools .zoomin').show();
$('#imagetools .zoomout').show();
$('#imagetools .ok').show();
$('#imagetools .ok').click(function(){
var img = $('#imageborder').find('img');
var offset = img.offset();
var pos = $('#imageborder').position();
offset.top = pos.top - offset.top;
offset.left = pos.left - offset.left;
var name = $('#imageborder').attr('name');
var scale = $('#imageborder').attr('scale');
var id = $('input[name=user_id]').val();
var hash = $('#hash').val();
var ret = xajax_settings_action({ xjxfun: 'settings_action' }, { parameters: ['saveimage',hash,id,name,offset,scale] }, { mode: 'synchronous' });
if(ret != 0)
{
$('#imagetools .help').hide();
$('#imagetools .zoomin').hide();
$('#imagetools .zoomout').hide();
$('#imagetools .ok').hide();
$('#imagetools .cancel').removeClass('cancel').addClass('new');
}
});
},
rename: function(name) {
// name in string format
// must return alternate name as string
var date = new Date();
// get file type
var typ = name.split('.',2);
// buffer name for save response
$('#imageborder').attr('name',date.getTime() + "." + typ[1]);
return date.getTime() + "." + typ[1];
},
// Called before each upload is started
beforeEach: function(file){
if(!file.type.match(/^image\//)){
jAlert('Only images are allowed!');
// Returning false will cause the
// file to be rejected
return false;
}
},
});
img_file.php
<?php
// Set the directory where files will be saved
$upload_dir = '/tmp/';
$allowed_ext = array('jpg','jpeg','png','gif');
if(strtolower($_SERVER['REQUEST_METHOD']) != 'post'){
exit_status('Error! Wrong HTTP method!');
}
if(array_key_exists('pic',$_FILES) && $_FILES['pic']['error'] == 0 ){
$pic = $_FILES['pic'];
if(!in_array(get_extension($pic['name']),$allowed_ext)){
exit_status('Only '.implode(',',$allowed_ext).' files are allowed!');
}
// Move the uploaded file from the temporary
// directory to the uploads folder:
if( move_uploaded_file($pic['tmp_name'], $upload_dir.$pic['name']) ){
exit_status('File was uploaded successfuly!');
}
}
exit_status('Something went wrong with your upload!');
// Helper functions
function exit_status($str){
echo json_encode(array('status'=>$str));
exit;
}
function get_extension($file_name){
$ext = explode('.', $file_name);
$ext = array_pop($ext);
return strtolower($ext);
}
?>
I use simpleimage.php from Simon Jarvis to scale and crop the image.
private function save_FileDB($id,$name,$pos,$scale)
{
$upload_dir = "/tmp/";
$response = new xajaxResponse();
$userid = $_SESSION['userid'];
$binFile = $upload_dir.$name;
if (isset($binFile) && $binFile != "none") {
$simage = new SimpleImage();
$simage->load($binFile);
$simage->scale($scale*100);
$simage->crop($pos[left],$pos[top],175,200);
$simage->save($binFile);
$binFile_size = filesize($binFile);
$ext = explode('.', $name);
$ext = strtolower( array_pop($ext) );
switch($ext){
case 'jpg': $binFile_type = 'image/jpeg'; break;
case 'png': $binFile_type = 'image/png'; break;
case 'gif': $binFile_type = 'image/gif'; break;
}
$data = addslashes(fread(fopen($binFile, "r"), $binFile_size ));
$qs = "update persondata set image='$data', imagetype='$binFile_type', imagesize='$binFile_size' where id=$id";
$ret = $this->exec_query($qs);
unlink($binFile);
$response->setReturnValue("1");
return $response;
}
$response->setReturnValue("0");
return $response;
}

reactive forms: use one validator for multiple fields

I'm using angular 2 reactive forms and made a validator for a date of birth field. The validator is working, but it turns out the date of birth field is split into three new field: year, month, day. They all have their own validators. My question is, how can I change my code so my original date of birth validator works on three fields.
my original validator that checks one field.
input(2000/12/12) is valid
export function dobValidator(control) {
const val = control.value;
const dobPattern = /^\d{4}\/\d{2}\/\d{2}$/ ;
const comp = val.split('/');
const y = parseInt(comp[0], 10);
const m = parseInt(comp[1], 10);
const d = parseInt(comp[2], 10);
const jsMonth = m - 1;
const date = new Date(y, jsMonth, d);
const isStringValid = dobPattern.test(control.value);
const isDateValid = (date.getFullYear() === y && date.getMonth() === jsMonth && date.getDate() === d);
return (isStringValid && isDateValid) ? null : { invalidDob: ('Date of birth not valid') };
};
new html with 3 fields
year has a validator that checks the year
day has a validator that checks if the input is between 1 and 31
month has a validator that checks if the input is between 1 and 12.
I want to combine the above input of the three field into a new string and use my original date of birth validator.
<label>Date of birth :</label>
<div>
<div class="col-xs-1">
<input required type="text" formControlName="day" class="form-control" placeholder="dd" id="day"/>
<p *ngIf="form.controls.day.dirty && form.controls.day.errors">{{ form.controls.day.errors.invalidDay }}</p>
</div>
<div class="col-xs-1">
<input required type="text" formControlName="month" class="form-control" placeholder="mm" id="month"/>
<p *ngIf="form.controls.month.dirty && form.controls.month.errors">{{ form.controls.month.errors.invalidMonth }}</p>
</div>
<div class="col-xs-2">
<input required type="text" formControlName="year" class="form-control" placeholder="yyyy" id="year"/>
<p *ngIf="form.controls.year.dirty && form.controls.year.errors">{{ form.controls.year.errors.invalidYear }}</p>
</div>
</div>
<div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</di>
I have created a validator for comparing two dates (their format is NgbDateStruct - as used in ng-bootstrap package's datepickers)
import { Directive, forwardRef, Attribute } from '#angular/core';
import { Validator, AbstractControl, NG_VALIDATORS, ValidatorFn } from '#angular/forms';
import { NgbDateStruct } from "#ng-bootstrap/ng-bootstrap";
import { toDate } from "../helpers/toDate";
export function dateCompareValidator(compareToControl: string, compareToValue: NgbDateStruct, compareType: string, reverse: boolean, errorName: string = 'dateCompare'): ValidatorFn {
return (c: AbstractControl): { [key: string]: any } => {
let compare = function (self: Date, compareTo: Date): any {
console.log('comparing ', compareType.toLowerCase());
console.log(self);
console.log(compareTo);
if (compareType.toLowerCase() === 'ge') {
if (self >= compareTo) {
return true;
} else {
return false;
}
} else if (compareType.toLowerCase() === 'le') {
if (self <= compareTo) {
return true;
} else {
return false;
}
}
return false;
};
// self value
let v = c.value;
// compare vlaue
let compareValue: Date;
let e;
if (compareToValue) {
compareValue = toDate(compareToValue);
} else {
e = c.root.get(compareToControl);
if (e) {
compareValue = toDate(e.value);
}
else {
// OTHER CONTROL NOT FOUND YET
return null;
}
}
let controlToValidate: AbstractControl = reverse ? e : c;
// validate and set result
let error = null;
let result = compare(toDate(c.value), compareValue);
if (result === true) {
console.log('clearing errors', compareToControl);
if (controlToValidate.errors) {
delete controlToValidate.errors[errorName];
if (!Object.keys(controlToValidate.errors).length) {
controlToValidate.setErrors(null);
}
}
else {
console.log('errors property not found in control', controlToValidate);
}
} else {
error = {};
error[errorName] = false;
controlToValidate.setErrors(error);
console.log(controlToValidate.errors);
console.log(controlToValidate.value);
console.log('Error Control', controlToValidate);
console.log('returning errors');
}
return reverse ? null : error;
}
}
Couldn't manage to modify much lot to best describe here as an answer but I believe you would get your query answered in this validator function code.
Note:
Function toDate() used in the code is a small function I created to convert NgbDateStruct into a javascript date object so that comparing dates can get easier. Here goes its implementation:
import { NgbDateStruct } from "#ng-bootstrap/ng-bootstrap"
export function toDate(ngbDate: NgbDateStruct): Date {
return ngbDate != null ? new Date(Date.UTC(ngbDate.year, ngbDate.month, ngbDate.day)) : null;
}