Im new to yii and ajax. I want to upload an image(profile pic) from a popup window. I used Ajax submit button.
But the file is not getting passed to the controller.
My code in the view is:
<?php
$form = $this->beginWidget('CActiveForm', array(
'id' => 'profile-update-form',
'enableAjaxValidation' => true,
'enableClientValidation' => true,
'action' => array('user/profileupdate'),
'htmlOptions'=> array('class' =>'form-horizontal')
));
?>
<?php
$model = User::model()->findByPk(Yii::app()->user->id);
$profile=UserProfile::model()->findByAttributes(array('user_id'=>$model->id));
if(!$profile)
$profile=new UserProfile;
?>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label text10">About me</label>
<div class="col-sm-8">
<?php echo $form->textArea($profile, 'about_me', array('class' => 'form-control form02')); ?>
</div>
<div class="req"> <?php echo $form->error($profile, 'about_me'); ?> </div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label text10">City</label>
<div class="col-sm-8">
<?php echo $form->textField($profile, 'city', array('class' => 'form-control form02', 'id' => 'inputCity')); ?>
</div>
<div class="req"> <?php echo $form->error($profile, 'city'); ?> </div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label text10">Phone</label>
<div class="col-sm-8">
<?php echo $form->textField($profile, 'phone', array('class' => 'form-control form02', 'id' => 'inputPhone')); ?>
</div>
<div class="req"> <?php echo $form->error($profile, 'phone'); ?> </div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label text10">Profile Picture</label>
<div class="col-sm-8">
<?php echo $form->fileField($profile,'profile_picture'); ?>
<?php
$this->widget('CMultiFileUpload', array(
'name' => 'images',
'accept' => 'jpeg|jpg|gif|png', // useful for verifying files
'duplicate' => 'Duplicate file!', // useful, i think
'denied' => 'Invalid file type', // useful, i think
));
?>
</div>
<div class="req"> <?php echo $form->error($profile, 'profile_picture'); ?> </div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label text10"></label>
<div class="col-sm-8">
<span>
<?php
echo CHtml::ajaxSubmitButton('Save', CHtml::normalizeUrl(array('user/profileupdate?rand=' . time())), array(
'dataType'=>'json',
'type'=>'post',
'success'=>'function(data) {
$("#AjaxLoader1").hide();
if(data.status=="success"){
$("#formResult1").html("profile settings changed successfully.");
$("#profile-update-form")[0].reset();
} else {
$.each(data, function(key, val) {
$("#profile-update-form #"+key+"_em_").text(val);
$("#profile-update-form #"+key+"_em_").show();
});
}
}',
'beforeSend'=>'function(){
$("#AjaxLoader1").show();
}'
), array(
'id' => 'profile-update', 'live' => false, 'class' => 'btn btn-s-md btn-info')
);
?>
...
And the code in my Controller is:
public function actionprofileupdate() {
$profile = UserProfile::model()->findByAttributes(array('user_id' => Yii::app()->user->id));
if (!$profile) {
$profile = new UserProfile;
$profile->create_time = time();
$profile->update_time = time();
}
if (isset($_POST['UserProfile'])) {
$profile->attributes = $_POST['UserProfile'];
$profile->about_me = $_POST['UserProfile']['about_me'];
$images = CUploadedFile::getInstance($profile,'profile_picture');
// print_r($_POST);
// print_r($_FILES);
// print_r($images);
// exit();
if (isset($images)) {
if(!is_dir(Yii::getPathOfAlias('webroot').'/images/profilepic/'. 'quy')) {
mkdir(Yii::getPathOfAlias('webroot').'/images/profilepic/'. $profile->profile_picture);
// the default implementation makes it under 777 permission, which you could possibly change recursively before deployment, but here�s less of a headache in case you don�t
}
foreach ($images as $image => $pic) {
echo $pic->name;if ($pic->saveAs(Yii::getPathOfAlias('webroot').'/images/profilepic/'.$pic->name)) {
$profile->profile_picture = $pic->name;
}
}
$profile->user_id = Yii::app()->user->id;
$profile->update_time = time();
$valid = $profile->validate();
$error = CActiveForm::validate(array($profile));
if ($error == '[]') {
$profile->save(false);
echo CJSON::encode(array('status' => 'success'));
Yii::app()->end();
} else {
$error = CActiveForm::validate(array($profile));
if ($error != '[]')
echo $error;
Yii::app()->end();
exit();
}
}
}
}
Please somebody help me to solve this problem.
When I am using ordinary submit button, the file is tranfering to the controller but when using AjaxSubmitButton the field profilepic is send as blank.
please try it by specifying the enctype in the html options like
'htmlOptions'=>array('enctype'=>'multipart/form-data'),
As of now it is not possible to upload a file or image using AjaxSubmitButton.
From the link below, it has been an open issues that the developer has not solved since 2015.
https://github.com/demogorgorn/yii2-ajax-submit-button/issues
Related
im following a video to creat JSON CRUD project, after watching the tuto i download the project file, started a server and put the files in it, but it was not working as i expect, after while i found out the problem could be the server quest method, in the vid, teacher click the submit button the method change to POST, but in my script it still apearing GET, how can i fix it ,
<?php
include 'partials/header.php';
function getUsers()
{
return json_decode(file_get_contents(__DIR__ . '/users/part.json'), true);
}
function getUserById($id)
{
$users = getUsers();
foreach ($users as $user) {
if ($user['id'] == $id) {
return $user;
}
}
return null;
}
function createUser($data)
{
$users = getUsers();
$data['id'] = rand(1000000, 2000000);
$users[] = $data;
putJson($users);
return $data;
}
function updateUser($data, $id)
{
$updateUser = [];
$users = getUsers();
foreach ($users as $i => $user) {
if ($user['id'] == $id) {
$users[$i] = $updateUser = array_merge($user, $data);
}
}
putJson($users);
return $updateUser;
}
$userId = 2;
$user = getUserById($userId);
if (!$user) {
include "partials/not_found.php";
exit;
}
$errors = [
'time' => "",
'part' => "",
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$user = array_merge($user, $_POST);
$isValid = validateUser($user, $errors);
if ($isValid) {
$user = updateUser($_POST, $userId);
uploadImage($_FILES['picture'], $user);
header("Location: index.php");
}
}
?>
<div class="container">
<div class="card">
<div class="card-header">
<h3>
<?php if ($user['id']): ?>
Update user <b><?php echo $user['id'] ?></b>
<?php else: ?>
Create new User
<?php endif ?>
</h3>
</div>
<div class="card-body">
<form method="POST" enctype="multipart/form-data"
action="">
<div class="form-group">
<label>Time</label>
<input name="name" value="<?php echo $user['time'] ?>"
class="form-control <?php echo $errors['time'] ? 'is-invalid' : '' ?>">
<div class="invalid-feedback">
<?php echo $errors['time'] ?>
</div>
</div>
<div class="form-group">
<label>part</label>
<input name="username" value="<?php echo $user['part'] ?>"
class="form-control <?php echo $errors['part'] ? 'is-invalid' : '' ?>">
<div class="invalid-feedback">
<?php echo $errors['part'] ?>
</div>
</div>
<button class="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
the original youtube link the submit part is at 26:00 ish
and my online version is here
I am creating a questionnaire and I am trying to record the answers obtained, I am showing the questions in a listview which are obtained from one table (question) and I want to record them in another table (answer) using active record. I have tried to add the active form inside the itemView class but the send button appears duplicated every time I have more than 1 question, then I try to add it outside the itemView and if the submit button appears only once but I can't get it the data listed in itemView because I don't know how to send the fields of the active form to get the data from the itamView, I tried to send them by the render of the itemView but it throws me the Undefined variable error.
View
<?php $form = ActiveForm::begin([
'enableClientValidation' => false,
'enableAjaxValidation' => true,]) ?>
<?= ListView::widget([
'layout' => '<div class="pull-left">{items}</div>',
'dataProvider' => $dataProvider,
'itemView' => function ($model, $key, $index, $widget) {
return $this->render('_answers',[
'model' => $model,
'index' => $index
]);
},
]); ?><div class="form-group">
<?php echo Html::submitButton('<span class="fa fa-plus"></span>'.' '.Yii::t('backend', 'Send') , ['class' => 'btn btn-primary']) ?>
View _answers
<td width="5%" class="vcenter" rowspan="3">
<span class="panel-title-address"><label class="control-label">Nr: <?php echo ($index+1); ?></label></span>
</td>
<td width="95%" class="vcenter">
<div class="form-group field-qquestion-0-title required">
<label class="control-label" for="qquestion-type_id"><?= Yii::t('backend', 'Question'.' : ')?></label>
</div>
<div class="form-group field-qquestion-0-title required">
<label class="control-label" for="qquestion-type_id"><?= $model->question ?></label>
</div>
<div class="col-md-4">
<?php echo $form->field($answer, 'answer')->textInput(['maxlength' => true]) ?>
</div>
</td>
what I want to obtain is the id and answer of each question to be able to register them in the answer table.
You are getting the undefined variable error because you are not passing the $form variable from your main view to _answers.php. You can pass it like this:
<?php $form = ActiveForm::begin([
'enableClientValidation' => false,
'enableAjaxValidation' => true,]) ?>
<?= ListView::widget([
'layout' => '<div class="pull-left">{items}</div>',
'dataProvider' => $dataProvider,
'itemView' => function ($model, $key, $index, $widget) use ($form) {
return $this->render('_answers',[
'form' => $form,
'model' => $model,
'index' => $index,
]);
},
]); ?><div class="form-group">
<?php echo Html::submitButton('<span class="fa fa-plus"></span>'.' '.Yii::t('backend', 'Send') , ['class' => 'btn btn-primary']) ?>
As for how to send multiple answers with the question ids you can either use the way mentioned in vvpanchev's answer or add a hidden field with question id. The _answers.php view with hidden field:
<td width="5%" class="vcenter" rowspan="3">
<span class="panel-title-address"><label class="control-label">Nr: <?php echo ($index+1); ?></label></span>
</td>
<td width="95%" class="vcenter">
<div class="form-group field-qquestion-0-title required">
<label class="control-label" for="qquestion-type_id"><?= Yii::t('backend', 'Question'.' : ')?></label>
</div>
<div class="form-group field-qquestion-0-title required">
<label class="control-label" for="qquestion-type_id"><?= $model->question ?></label>
</div>
<div class="col-md-4">
<?php
//set the question's id to answer model if you haven't done that already
$answer->question_id = $model->id;
//output the hidden input with question id
echo \yii\helpers\Html::activeHiddenInput($answer, "[$index]question_id");
?>
<?php echo $form->field($answer, "[$index]answer")->textInput(['maxlength' => true]) ?>
</div>
</td>
If you use the hidden field approach then in your controller you can use \yii\base\Model::loadMultiple() method to load the data into answer models. You can also use \yii\base\Model::validateMultiple() for validation. I'm assuming the name for answer model class is Answer.
$count = count(\Yii::$app->request->post('Answer', []));
$answers = [];
for ($i = 0; $i < $count; $i++) {
$answers[] = new Answer();
}
if (
\yii\base\Model::loadMultiple($answers, \Yii::$app->request->post())
&& \yii\base\Model::validateMultiple($answers)
) {
// ... save your answers and/or do other things needed
}
Change your answer input like this and you'll have answer input for every question:
<?php echo $form->field($answer, '['.$model->id.']answer')->textInput(['maxlength' => true]) ?>
When you submit teh form, it will be submitted with all answer questions. So you can check and save the $_POST like this:
if(isset($_POST['Answer']) and !empty($_POST['Answer'])){
foreach($_POST['Answer'] as $question_id => $answer){
//save your answer to your question
}
}
You will have to change you ajaxvalidation too like this foreach
I have following code in my view:
<?= $form->labelEx($model, 'p_2_1', array('class' => 'col-xs-12 col-sm-2 control-label')) ?>
<div class="col-xs-12 col-sm-3">
<?= $form->dropDownList($model, 'p_2_1',array_combine($model->getData('money'),$model->getData('money')), array('class' => 'form-control')) ?>
<?= $form->error($model, 'p_2_1') ?>
</div>
<?= $form->labelEx($model, 'p_3_1', array('class' => 'col-xs-12 col-sm-2 control-label')) ?>
<div class="col-xs-12 col-sm-3">
<?= $form->dropDownList($model, 'p_3_1',array_combine($model->getData('money'),$model->getData('money')), array('class' => 'form-control')) ?>
<?= $form->error($model, 'p_3_1') ?>
</div>
And in my model, I have following code:
public function getData($property) {
$data = array(
'money' => array(
Yii::t('plaintinfo', 'RUB'),
Yii::t('plaintinfo', 'USD'),
Yii::t('plaintinfo', 'EURO'),
),
);
return $data[$property];
}
I need to develop JavaScript code when user p_2_1 value changes, p_3_1 value also changes and becomes the same as p_2_1 value. (for example, if user chooses USD from the drop down list p_2_1, the value of p_3_1 will be USD automatically (the same as p_2_1(USD) ). How can I do it?
You can solve it with following Javascript code. You could also change your ids to more understandable.
<script>
$("#p_2_1").change(function(){
var selected = $("#p_2_1 option:selected").val();
var elementToChange = document.getElementById('p_2_1');
elementToChange.value = selected;
});
</script>
I have an ajax uploader in my site that uploads the image but i have to refresh the page in order to view that uploaded image.
How i can avoid this refresh?
My js is
$(function(){
var ul = $('#editprofile-form ul');
$('#drop a').click(function(){
// Simulate a click on the file input button
// to show the file browser dialog
$(this).parent().find('input').click();
});
// Initialize the jQuery File Upload plugin
$('#editprofile-form').fileupload({
// This element will accept file drag/drop uploading
dropZone: $('#drop'),
// This function is called when a file is added to the queue;
// either via the browse button, or via drag/drop:
add: function (e, data) {
var tpl = $('<li class="working"><input type="text" value="0" data-width="48" data-height="48"'+
' data-fgColor="#0788a5" data-readOnly="1" data-bgColor="#3e4043" /><p></p><span></span></li>');
// Append the file name and file size
tpl.find('p').text(data.files[0].name)
.append('<i>' + formatFileSize(data.files[0].size) + '</i>');
// Add the HTML to the UL element
data.context = tpl.appendTo(ul);
// Initialize the knob plugin
tpl.find('input').knob();
// Listen for clicks on the cancel icon
tpl.find('span').click(function(){
if(tpl.hasClass('working')){
jqXHR.abort();
}
tpl.fadeOut(function(){
tpl.remove();
});
});
// Automatically upload the file once it is added to the queue
var jqXHR = data.submit();
var img = document.getElementById('image-placeholder').innerHTML ;
console.log(img);
},
progress: function(e, data){
// Calculate the completion percentage of the upload
var progress = parseInt(data.loaded / data.total * 100, 10);
// Update the hidden input field and trigger a change
// so that the jQuery knob plugin knows to update the dial
data.context.find('input').val(progress).change();
if(progress == 100){
data.context.removeClass('working');
}
},
fail:function(e, data){
// Something has gone wrong!
data.context.addClass('error');
}
});
// Prevent the default action when a file is dropped on the window
$(document).on('drop dragover', function (e) {
e.preventDefault();
});
// Helper function that formats the file sizes
function formatFileSize(bytes) {
if (typeof bytes !== 'number') {
return '';
}
if (bytes >= 1000000000) {
return (bytes / 1000000000).toFixed(2) + ' GB';
}
if (bytes >= 1000000) {
return (bytes / 1000000).toFixed(2) + ' MB';
}
return (bytes / 1000).toFixed(2) + ' KB';
}
});
and my form is
<div class="adminform_wrapp">
<?php
$form = $this->beginWidget('CActiveForm', array(
'id' => 'editprofile-form',
'enableAjaxValidation' => false,
'clientOptions' => array(
'validateOnSubmit' => true,
),
'enableClientValidation' => true,
'focus' => array($model, 'first_name'),
'htmlOptions' => array(
'enctype' => 'multipart/form-data'
)
));
//echo $form->errorSummary($model);
?>
<div class="adminform_row">
<?php echo $form->errorSummary($model); ?>
</div>
<div class="adminform_row errorSummary">
<?php echo $response_msg; ?>
</div>
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_First_Name">First Name: <span class="required">*</span></label>
<?php echo $form->textField($model, 'first_name', array('value' => $data['first_name'])); ?>
<?php $form->error($model, 'first_name'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_Last_Name">Last Name: <span class="required">*</span></label>
<?php echo $form->textField($model, 'last_name', array('value' => $data['last_name'])); ?>
<?php $form->error($model, 'last_name'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_Email">Email: <span class="required">*</span></label>
<?php echo $form->textField($model, 'email', array('value' => $data['email'], 'readonly' => true)); ?>
<?php $form->error($model, 'email'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_Phone_No">Phone No: <span class="required">*</span></label>
<?php echo $form->textField($model, 'phone_no', array('value' => $data['phone_no'])); ?>
<?php $form->error($model, 'phone_no'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label>Username:</label>
<span class="profile-username"><?php echo $data['username']; ?></span> </div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label>Edit Profile picture:</label>
<span class="image-placeholder" id="image-placeholder">
<?php
if (file_exists(Yii::getPathOfAlias('webroot') . '/themes/karmora/images/users/' . $data['image']) && $data['image'] != null) {
$u_image_path = $this->theme_baseurl . '/images/users/' . $data['image'];
} else {
$u_image_path = $this->theme_baseurl . '/images/users/default-thumb.png';
}
?>
<img src="<?php echo $u_image_path; ?>" style="width:96px; height:96px;"/>
</span>
<div id='file_browse_wrapper'>
<?php
//echo $form->labelEx($model, 'image');
echo $form->fileField($model, 'upl', array('id' => 'file_browse'));
echo $form->error($model, 'upl');
?>
</div>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<?php echo $form->labelEx($model, 'Address', array('class' => 'fieldname')); ?>
<?php echo $form->textField($model, 'address', array('value' => $data['address'])); ?>
<?php $form->error($model, 'address'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_Country">Country: <span class="required">*</span></label>
<select class="error" onchange="print_state('Users_state', this.selectedIndex);" id="Users_country" name ="Users[country]"></select>
<?php $form->error($model, 'country'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_State">State: <span class="required">*</span></label>
<select name ="Users[state]" id ="Users_state"></select>
<script language="javascript">print_state("Users_state", '', "<?php echo $data['state'] ?>");</script>
<?php $form->error($model, 'state'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_City">City: <span class="required">*</span></label>
<?php echo $form->textField($model, 'city', array('value' => $data['city'])); ?>
<?php $form->error($model, 'city'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<label class="fieldname required" for="Users_Zipcode">Zipcode: <span class="required">*</span></label>
<?php echo $form->textField($model, 'zipcode', array('value' => $data['zipcode'])); ?>
<?php $form->error($model, 'zipcode'); ?>
</div>
<!--adminform_row-->
<!--adminform_row-->
<div class="adminform_row">
<input type="submit" class="adminupdate_btn" value="Update">
<input type="reset" class="admincancel_btn" value="Cancel">
</div>
<!--adminform_row-->
<?php $this->endWidget(); ?>
</div>
Try adding a success event to your fileupload. If you register a success, then just replace the picture with the one already in your jqXHR object. Not sure what plugin you're using but that shouldn't be very hard to accomplish.
Update at bottom of post:
I have a page where when a button is pressed, a new window will popup with a form. Below is where I open the popup.
<a><span class="add-on"><i class="icon-plus" style="color: green;" onclick="window.open('<?php echo $this->Html->Url(array('controller' => 'customers', 'action' => 'add?popup')); ?>', 'Add Customer', 'height=630, width=430')"></i></span></a>
When the form is submitted, I need to close that window. The problem I am having now is that the window is closing before the form is fully getting submitted.
Popup Window Code:
<fieldset style="padding-left: 15px;">
<legend>Add Customer</legend>
<?php echo $this->Form->create('Customer', array('inputDefaults' => array('div' => false, 'label' => false))); ?>
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-user"></i></span>
<?php echo $this->Form->input('first_name', array('placeholder' => 'First Name')); ?>
</div>
</div>
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-user"></i></span>
<?php echo $this->Form->input('last_name', array('placeholder' => 'Last Name')); ?>
</div>
</div>
<div class="input-prepend">
<span class="add-on"><i class="icon-globe"></i></span>
<?php echo $this->Form->input('location', array('placeholder' => 'Location')); ?>
</div>
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-phone"></i></span>
<?php echo $this->Form->input('phone_number', array('class' => 'maskedPhone', 'placeholder' => 'Phone Number')); ?>
</div>
</div>
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-envelope"></i></span>
<?php echo $this->Form->input('email', array('placeholder' => 'Email')); ?>
</div>
</div>
<!-- Close Form -->
<?php echo $this->Form->end(array(
'label' => 'Save Customer',
'id' => 'saveCust',
'class' => 'btn btn-primary',
'div' => false
)); ?>
<?php echo $this->Html->script('jquery.maskedinput.min', array('inline' => false));
echo $this->Html->script('maskedinput', array('inline' => false));
$this->start('script'); ?>
<script type="text/javascript">
$(document).ready(function () {
inputs.maskedInputs();
// Get queryString to close popup window
var queryString = window.location.search.substring(1);
if (queryString == "popup"){
$('#CustomerAddForm').submit(function() {
window.close();
});
}
});
</script>
<?php $this->end(); ?>
Trying to use Ajax call now with no luck:
$('#saveCust').click(function() {
alert("test");
$.ajax({
type: "POST",
url: "<?php echo $this->Html->Url(array('controller' => 'Customers', 'action' => 'add')); ?>",
data: $('#CustomerAddForm').serialize(),
success: function(data)
{
alert("Test");
window.close();
}
})
});
return false;
You have a race condition and the window.close will always win. You need to close when the form is DONE submitting.
Either convert it over to use an Ajax call or call the window.close() in the response of the form submission.