Add elements to a Yii 1.1 form dynamically - php

I was trying to add elements dynamically to a yii 1.1 TbActiveForm. I tried two methods to do this, but fails when it comes to validation. Please see my methods below.
Method 1 # Clone elements; modify id's
This is how i create form
$form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'id' => 'form-name',
'enableClientValidation' => true,
'clientOptions' => array(
'validateOnSubmit'=>true
),
'action' => $this->createUrl('test/manageusers')
));
On clicking on the "Add more" button, this script will duplicate elements
$('#add-comp-user').on('click', function(){
// template
var html = $('.add-comp-users-wrapper').first().clone();
// next element index
var next_index = // find last element's index attribute
// update element id's
html.find(':input').each(function(){
// update name
// update id
});
// insert to DOM
});
Method 2 #Ajax method
Here, on cliking on "Add more" button, i will render form elements with new id and insert to DOM
Both these methods failed in validation. How can i include a newly added element to Yii validation ?

Related

Removing a field from a form generated by YII2 ActiveForm

I have a form generated using YII2 ActiveForm. there are some field I need to be on the if I select certain options , or need to have them removed if I select some other option.
For e.g. I Have a dropdown AccountType, with two options "individual" and "company".
If the user selects "individual" some fields on the form needs to go away say company name, and some other fields need to appear such as First name, last name. Initially when the display the form , only the Account Type field is there.
below is the code I have at the moment
<?php
$form = ActiveForm::begin(['id' => 'account-setup-form']); ?>
echo $form->field($modelAccMain, 'account_type')
->widget(Select2::classname(), [
'data' => $accountTypeArray,
'options' => ['placeholder' => 'Select account type'],
]);
echo $form->field($modelUsers, 'firstname')->textInput()
->hint('')->label('First Name');
echo $form->field($modelUsers, 'lastname')->textInput()
->hint('')->label('Last Name');
<?php ActiveForm::end(); ?>
Any help is greatly appreciated.
You can use scenarios for that, first define them in your model and than you can use a if statement in your view
if ($model->isAttributeActive('attribute_name')) {
But like #nterms wrote, if you want the user to be able to switch on the client side, javascript would be better.
Defining scenarios also helps with the validation (only active attributes will be validated).
p.s. Don't forget to set the scenario in your controller
$model = new MyModel(['scenario'=>'my_scenario']);
The way i would handle it is with jquery hide and show using the change event of the dropdown,
In your javascript
Assuming that the data in the select 2 widget is in the form of array
eg:
[1=>"first-item",2=>"second-item",...]
$(document).ready(function(){
var id= //check the id of the select2
on the inspect element id using chrome;
$("#id").on("change", function(){
if(id.value==1){
//show a div
}else{
//hide a div
}
//for multiple values better use switch
like this
switch(id){
case 1:{
$("#divid").show();
......
}
}
})
})
I hope you get the idea,
For the select 2 id you can set it via
echo $form->field($modelAccMain, 'account_type')
->widget(Select2::classname(), [
'data' => $accountTypeArray,
'options' => ['placeholder' => 'Select account type',"id"=>"mypreffereid"],
]);

Symfony collection field type within a collection field type: add / remove elements

i've got a problem regarding the symfony collection field type.
Working with one collection type (adding, removing items) is clear due to the cookbook entry of symfony for working with collection field type. For me, the problem appears when i want to add / remove items to a collection type within a collection type. Example:
I have an addresses collection that gets added to my Contact Type class
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('addresses', 'collection', array(
'type' => new AddressType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
))
;
}
This addresses collection gets as type "AddressType", which looks the following:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// name = country
->add('name', 'text')
->add('cities', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
))
;
}
My goal is now to be able to add / remove new "addresses" to the form (which is working due to the cookbook entry), BUT for each address, i also want to add / remove cities.
Question:
When following the example of the symfony page, i should add in the jquery:
/ addresses
$collectionHolder = $('#ecommerce_user_contact_addresses');
the collection holder. For the addresses, i know what to write here because the data-prototype generated in the widget in the view has that id.
But what should I write here for the inner collection?
When i write:
// cities
$collectionHolderCities = $('#ecommerce_user_contact_addresses___name___cities');
, which also comes from the data-prototype of the parent div that describes in this case the "cities" element structure", i don't know now how to add / remove new elements of that.
When calling:
var prototype = $collectionHolderCities.data('prototype');
in my 'addCityForm(..)' method, the console logs me the variable 'prototype' as undefined. When i logged the prototype in the other method 'addAddressForm', it correctly showed me the prototype structure.
What should i define as collection holder of the "inner collection" for the cities, and what do i need to change in addition to get this working ?
Address (with country name)
City
City
City
Address (with country name)
City
City
...
Regards.
EDIT:
I have implemented the js function to add a City now this way, and it is working for the first address that is displayed on document.ready. I don't know if this is the best way to handle it, any other approaches are welcome :)
I get the collection Holder in document.ready with that:
// cities
$collectionHolderCities = $('#ecommerce_user_contact_addresses_0_cities');
Then, in my addCityForm function, i do some replacement to get the newForm right:
function addCityForm($collectionHolderCities, $newLinkCity) {
// Get the data-prototype explained earlier
var prototype = $collectionHolderCities.data('prototype');
// get the new index
var index = $collectionHolderCities.data('index');
// replace 'cities_0' default value in prototype with 'cities__counter'
prototype = prototype.replace("cities_0", "cities__counter__");
prototype = prototype.replace("cities_0", "cities__counter__");
// replace '0label__' with '__counter__label__'
prototype = prototype.replace("0label__", "__counter__label__");
// replace [cities][0] with [cities][__counter__]
prototype = prototype.replace("[cities][0]", "[cities][__counter__]");
// Replace '__counter__' in the prototype's HTML to
// instead be a number based on how many items we have
var trans = $('#city-trans').val();
var newForm = prototype.replace(/__counter__/g,index);
// Replace cities0 (cities + index) with cities_0 (cities + "_" + index)
newForm = newForm.replace('cities'+index, 'cities_' + index);
newForm = newForm.replace('cities'+index, 'cities_' + index);
// var repStr = '0label__';
//newForm = newForm.replace(repStr, trans + ':');
// increase the index with one for the next item
$collectionHolderCities.data('index', index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormS = $('<span></span>').append(newForm);
$newLinkCity.before($newFormS);
// add a delete link to the new form
addCityFormDeleteLink($newFormS);
}
The problem now is :
When i add a new 'address', i cannot add cities to this new added address. There is no link or a default city input field. How can i handle that ?
Adding new cities only works for the first address that is displayed on document.ready:
jQuery(document).ready(function() {
// addresses
$collectionHolder = $('#ecommerce_user_contact_addresses');
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($newLink);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$addAddressLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addAddressForm($collectionHolder, $newLink);
});
// add blank collections
addAddressForm($collectionHolder, $newLink);
// add city
// cities
$collectionHolderCities = $('#ecommerce_user_contact_addresses_0_cities');
// add the "add a tag" anchor and li to the tags ul
$collectionHolderCities.append($newLinkCity);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolderCities.data('index', $collectionHolderCities.find(':input').length);
$addCityLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
addCityForm($collectionHolderCities, $newLinkCity);
});
addCityForm($collectionHolderCities, $newLinkCity);
});
The 'addAddressForm()' function is like in the symfony cookbook entry.
Don't know how to add cities now for added addresses.. :/

CakePHP prepopulate form with data from a link

Assume I'm in my items controller.
Ok say I am in my view action (the url would be something like /items/view/10012?date=2013-09-30) which lists a list of items that belongs to a client on a given date.
I want to link to add a new item. I would use the htmlhelper like so:
echo $this->Html('action'=>'add');
In my add action I have a form which has fields like client_id and item_date.
When I'm in my view action I know these values as I am viewing the items for a specific client on a specific date. I want to pass these variables to my add action so it will prefill those fields on the form.
If I add a query string in my link ('?' => array('client_id'=>$client_id)) it breaks the add action as it will give an error if the request is not POST. If I use a form->postLink I get another error as the add action's POST data must only be used for adding the record, not passing data to prefill the form.
I basically want to make my link on the view page pass those 2 variables to the add action in the controller so I can define some variables to prefill the form. Is there a way to do this?
Here is my add controller code. It may differ in content a bit from my question above as I have tried to simplify the question a bit but the concept should still apply.
public function add(){
if ($this->request->is('post')) {
$this->Holding->create();
if ($this->Holding->save($this->request->data)) {
$this->Session->setFlash(__('Holding has been saved.'), 'default', array('class' => 'alert alert-success'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('Unable to add your holding.'), 'default', array('class' => 'alert alert-danger'));
}
$this->set('accounts', $this->Holding->Account->find('list'));
$sedol_list = $this->Holding->Sedol->find('all', array(
'fields' => array(
'id', 'sedol_description'
),
'recursive' => 0,
'order' => 'description'
)
);
$this->set('sedols', Hash::combine($sedol_list, '{n}.Sedol.id', '{n}.Sedol.sedol_description') );
}
Why not use proper Cake URL parameters?
echo $this->Html->link('Add Item', array(
'action' => 'add',
$client_id,
$item_date
));
This will give you a much nicer URL like:
http://www.example.com/items/add/10012/2013-09-30
And then in your controller, you modify the function to receive those parameters:
public function add($client_id, $item_date) {
// Prefill the form on this page by manually setting the values
// in the request data array. This is what Cake uses to populate
// the form inputs on your page.
if (empty($this->request->data)) {
$this->request->data['Item']['client_id'] = $client_id;
$this->request->data['Item']['item_date'] = $item_date;
} else {
// In here process the form data normally from when the
// user has submitted it themselves...
}
}

Drupal 7 collapsible fieldset issues with ajax

I am sending data via ajax to my PHP callback function inside my custom module... Everything is working great. The problem I am having is: the html i am returning and rendering in the ajax complete funciton is composed of fieldsets which wont expand/collapse. Things i have tested :
1). If I render the same html ( fieldsets ) into one of the custom blocks in that same module, they work great, the collapse and expand.
2). I have tried including this inside the callback function,
drupal_add_js('misc/form.js');
drupal_add_js('misc/collapse.js');
inside the fieldset render array,
return array(
'#type' => 'fieldset',
'#title' => t($title),
'#attributes' => array(
'class' => $class,
),
'#attached' => array(
'js' => array(
'misc/form.js',
'misc/collapse.js',
),
),
);
The
$class
variable contains collapsed and collapsible. I have also tried using
#theme => 'fieldset'
Instead of #type => 'fieldset'
the javascript files
misc/collapse.js
misc/form.js
are already being included from a different function inside my module. And looking at the source of the page, are indeed being included to the head of the page.
This has to be related to bringing them in via ajax, I know when you bring elements into the document via ajax, in-order to set events for those new elements you have to use
$(document).on('event', 'class', function(){
//code here
});
and I am wondering if this could be the reason that the fieldsets wont collapse/expand.
If you get data with AJAX with jQuery once you have to use Drupal.behaviors like this:
Drupal.behaviors.MYBEHAVIOR = {
attach: function (context, settings) {
$('MYELEMENT.MYBEHAVIOR', context).once('MYBEHAVIOR', function () {
// Apply the MYBEHAVIOR effect to the elements only once.
});
}
};
See more at the js documentation https://drupal.org/node/756722

Yii CGridView - Custom Columns

been looking for a solution to add a feature for "Custom Columns"... Meaning, I present a list of columns that I can show the user and he selects the ones he wants to see and after the selection the table is updated and add/removes the needed columns.
Didn't find anything on Google (perhaps it has a different name than what I was looking for...)
Anyone has an Idea on how it can be accomplished?
Thanks in advance!
This is not a complete sample, but can give you some clues on how to implement it. You've to define some kind of form to collect the data about how your grid has to be rendered. I recommend you to create a CFormModel class if there are more than 3 input fields. Create a view file with the form and a div or renderPartial of a file containing a grid:
$form = $this->beginWidget('CActiveFormExt');
echo $form->errorSummary($model);
echo $form->labelEx($model,'column1');
echo $form->dropDownList($model
echo $form->error($model,'column1');
echo CHtml::ajaxSubmitButton('UpdateGrid',array('controller/grid'),
array('update'=>'#grid'),
$this->endWidget();
// you can render the 'default options' before any ajax update
$this->renderPartial('_grid',array($customColumns=>array('id','name'),'dataProvider'=>$dataProvider));
In the _grid.php view file:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'grid',
'dataProvider'=>$dataProvider,
'columns' => $customColumns;
));
In the controller:
function actionGrid(){
// recover the form data, and build the custom columns array
$customColumns = array();
$customColumns[] = '.....';
$dataProvider = ...;
$this->renderPartial('_formTrabajo', array('customColumns' => $idSiniestro, 'dataProvider' => $dataProvider'), false);
}
When you click the ajaxSubmitButton, the form is sent to the url specified through ajax, and the reply from the controller must contain the renderPartial of the view containing the grid, so the jQuery call can replace the html correctly. You must pass an array from your controller to the partial view of the grid, with the custom list of columns you want to display.

Categories