Triggering submit event after change when uploading image on Yii2 framework - javascript

I am trying to save an image type file by using jquery to trigger the submit. I encountered an issue where it triggers even when the change event is not done.
View create.php:
<?php \yii\bootstrap4\ActiveForm::begin([
'options' => [
'enctype' => 'multipart/form-data',
'id' => 'dynamic-form'
]
]) ?>
<button class="btn btn-primary btn-file">
Select Thumbnail
<input type="file" id="recipeThumbnail" name="recipe">
</button>
<?php \yii\bootstrap4\ActiveForm::end() ?>
In this view, i specify the id to call it in the javascipt file.
Js app.js:
$(function () {
'use strict';
$('#recipeThumbnail').change(ev => {
$(ev.target).closest('form').trigger('submit');
})
});
From my understanding here, the trigger('submit') should only run when there is a change which in this case an image file is selected. However, i suspect the event is triggered and it caused an error of Attempt to read property "name" on null from the model below on the line $this->name = $this->recipe->name;. My other suspicion would be error in the model but i cant wrap my head around where or how it could be.
Model recipe.php:
public function save($runValidaiton = true, $attributeNames = null)
{
$isInsert = $this->isNewRecord;
if ($isInsert) {
$this->recipe_id = Yii::$app->security->generateRandomString(16);
$this->name = $this->recipe->name;
}
$saved = parent::save($runValidaiton, $attributeNames);
if (!$saved) {
return false;
}
if ($isInsert) {
$recipePath = Yii::getAlias('#frontend/web/storage/thumbnail/' . $this->recipe_id . '.jpg');
if (!is_dir(dirname($recipePath))) {
FileHelper::createDirectory(dirname($recipePath));
}
$this->recipe->saveAs($recipePath);
}
return true;
}
Controller RecipeController.php:
public function actionCreate()
{
$model = new Recipe();
$model->recipe = UploadedFile::getInstanceByName('recipe');
if ($this->request->isPost) {
if (Yii::$app->request->isPost && $model->save()) {
return $this->redirect(['view', 'recipe_id' => $model->recipe_id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
I hope i explained this well. Is this error actually caused by the js or something else and any insight on this issue is much appreciated:D

Insights from #ChrisG and #CBroe,
The issue was wrapping the input tag in a button tag which will click both button and input at the same time causing the error. An easy fix would be adding type="button" in the button tag.
But this is actually a broken HTML as an interactive element should not be nested into each other. The fix to this would be using form and labels with the button so that it is a decent html.

Related

Laravel - JS : search function without reloading page, return html

on a small project using laravel and javascript, I would like to implement a search functionality
For this, I would like that once the search is submitted, the page content changes without reloading
So I have a first method in my controller, which renders the page view complete with my data
In the page template, I included a file of partials, containing only my foreach loop and the associated html
Here is the controller method
public function __invoke(MyService $myService)
{
return view('posts.index', [
'posts' => $myService->getAll(),
]);
}
and my partials present in posts.index
#foreach($posts as $post)
<div class="">
{{ $post->name }}
<p class="my-4">
{{ str($post->data)->limit(150) }}
</p>
</div>
#endforeach
So, in my posts.index, I add this JS
var search = document.getElementById("search");
var by = document.getElementById("by");
var form = document.getElementById("form");
form.addEventListener("submit", function(evt) {
evt.preventDefault();
fetch('/search?search=' + search.value + '&by=' + by.value)
.then(response => response.json())
.then(data => {
var elem = document.querySelector('#result');
elem.innerHTML = JSON.stringify(data.html)
});
});
The #result element is where I'm including the partials
There is my search function
public function search(Request $request){
$by = $request->input('by');
switch ($by){
case 'name':
$service = new MyService();
$result = $service->getPostsForName($request->input('search');
$html = view('partials.list', ['posts' => compact('result')])->render();
return response()->json(compact('html'));
break;
}
}
The two methods of the controller return me an Array of Post (my model)
But when I run a search I always get the following error
attempt to read property "url" on array in file
I can't understand why, could you help me please ?

How to toggle a class based on a variable passed from Laravel using a Vuejs component

This is the 3rd attempt for me to get some help on this issue, for so long I've been trying to toggle an id using Vuejs, I just want that id to change based on a boolean value passed on from Laravel, I managed to do it without components but I ran into a problem, there's multiple buttons on the page, when one gets their id updated the others do as well, so I thought maybe a component could solve this, I just can't get it to work.
Here's the blade template, this is inside a table inside a foreach loop that has access to a $courses variable:
courses.blade:
<form method="POST" action="{{ route('course.completed', $course->name) }}" id="form-submit">
{{ csrf_field() }}
#if ($course->pivot->completed == true)
<course-buttons id="this.greenClass.aClass">Done</course-buttons>
#else
<course-buttons id="this.redClass.aClass">Not Yet</course-buttons>
#endif
</form>
this is app.js:
require('./bootstrap');
Vue.component('course-buttons', require('./components/course-buttons.vue'))
var vm = new Vue({
el: '#app'
});
And this is the course-buttons.vue file:
<template>
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id=id><slot></slot></button>
</template>
<script>
export default {
props: ['id'],
data: function() {
return {
greenClass: {aClass: 'coursetrue', text: 'Done!'},
redClass: {aClass: 'coursefalse', text: 'Not Yet!'}
};
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then(function (response){
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
return [
vm.redClass.aClass = 'coursetrue',
vm.redClass.text = 'Done!',
vm.greenClass.aClass = 'coursetrue',
vm.greenClass.text = 'Done!'
]
} else {
return [
vm.greenClass.aClass = 'coursefalse',
vm.greenClass.text = 'Not Yet',
vm.redClass.aClass = 'coursefalse',
vm.redClass.text = 'Not Yet'
]
}
});
}
}
}
</script>
First I know that my code isn't good, that's why I asked for help many times but with no answers at all, so if you have any tips that might help me get this code cleaner, even change it totally but just get the job done, I'm all ears.
The errors I'm getting right now is first the #click.prevent is an invalid expression, tried moving that to the tag, it doesn't do anything over there so I had that going before and now I lost it as well, also I get an error that the id is not defined on the instance, although I defined the props and data in the vue component.
if you're wondering why do I even assign the id on the tag itself and not the component, it's because of how my code is structured,e.g "If the value is true then load the tag with that id, otherwise load it with that id", once again if you can help me do this in a completely different way I'll be grateful.
One error I see is you have to use v-bind:id here, as you are assigning a vue variable:
<course-buttons v-bind:id="this.greenClass.aClass">Done</course-buttons>
One more is in onSubmit method, you dont have to return anything, and also you are using vm there, instead of that you can just do following:
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then((response) => {
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
this.redClass.aClass = 'coursetrue',
this.redClass.text = 'Done!',
this.greenClass.aClass = 'coursetrue',
this.greenClass.text = 'Done!'
} else {
this.greenClass.aClass = 'coursefalse',
this.greenClass.text = 'Not Yet',
this.redClass.aClass = 'coursefalse',
this.redClass.text = 'Not Yet'
}
});
}
Here I have also used arrow syntax, here is why.

If statement cakephp input value and run a javascript code

I am trying to make an if statement with cakePHP but I'm really an amateur. I checked the cookbook and stackoverflow but couldn't find it. (using 2.x.x version of cake)
So what I'm trying to do is:- if the ticket-1-amount is not zero, remove the invisible class.
Something I tried but didn't work:-
if ( $('#ListTypeTicket-1-amount').val() != '' ) {
$("#invisibleBox").removeClass("invisible");
}
Also tried this:-
if (empty($this->request->data ['ticket-1-amount'] != 0)) {
echo '$("#invisibleBox").removeClass("invisible");</script>';
} ;
My cakePHP form:-
<?=$this->Form->input(
'ticket-1-amount',
array('label' => false,
'class' => 'ticket-1-amount',
'id' => 'ticket-1-amount')
); ?>
This is the actual div
<div id='invisibleBox' class="invisible">
..........
</div>
Keep things simple, Just try:
<div id='invisibleBox' class="<?php if($this->request->data['ticket-1-amount'] == 0) {echo 'invisible'}; ?>">
..........
</div>
Create a JavaScript file and put the code below inside it.
App = {
init: function () {
this.checkTicketAmount();
},
checkTicketAmount: function(){
if ($('#ticket-1-amount').val() != '0') {
$("#invisibleBox").removeClass("invisible");
}
}
};
$(document).ready(function () {
App.init();
});
After the page is loaded, jQuery will check the ticket amount value and remove the invisible class, or not.

Click a button to change the text of text input

I have this part of code in the Codeigniter controller named 'ajaxcalls' :
public function show_contact_persons($client_id) {
$data['contact_persons'] = $this->common_model->select_records('ci_contact_persons', 'client_id', $client_id);
//dump($data);
function echoArray ($array) {
foreach ($array as $key => $value)
{
if ( true == is_array($value) )
{
echoArray($value);
}
else
{
if ($key == 'contact_person_name') {
echo '<input type="button" value="'.$value.'" onclick="changeContact(987)" /><br />';
}
}
}
}
echoArray($data);
}
and then the JS part at the page which is calling the PHP file :
function changeContact(contact_name) {
document.getElementById('contact_person').value = contact_name;
}
I put the number '987' in this example because the code works only with NUMBERS. When I put text inside the bracket, or what I REALLY want to do :
onclick="changeContact('.$value.')"
then the script doesn't work. ONLY with numbers. What should I change to make it work with strings?
You are very close.
Use this instead :
onclick="changeContact("'.$value.'")"
Whats happening is the JS is rendering it as changeContact(stringValue); so its expecting it to be a defined variable.
Adding the quotations will pass it into the function as a string which will render as : changeContact("stringValue");

Adding JScript to a prestashop CMS page

prestashop v1.5.4.0
I want to add this click to open element made from CSS, HTML and JS located here http://codepen.io/anon/pen/Gqkxv
function openDoor(field) {
var y = $(field).find(".thumb");
var x = y.attr("class");
if (y.hasClass("thumbOpened")) {
y.removeClass("thumbOpened");
}
else {
$(".thumb").removeClass("thumbOpened");
y.addClass("thumbOpened");
}
}
what is the best method to place this in to a CMS page
My guess is since the CMS pages strip out most javascript tags and don't seem to allow you to attach exernal js files you will need to create an override of the cmsController.php.
You would need to create your external js file and css file and save them in the theme's js directory and css directory. The setMedia method is used to attach style/js files when that controller is called. You can override the cmsController.php and add this to the setMedia method
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
I believe that should work however, this will add these files to every cms page. The only way I can think to get around that is by getting the ID of the cms page(s) that you want it to appear on and run an if state on your addJS and addCSS functions.
example: You want it to show up on id_cms 4
if ((int)Tools::getValue('id_cms') == 4) {
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
}
or you want it to show on id_cms 4 and id_cms 6
if ((int)Tools::getValue('id_cms') == 4 || (int)Tools::getValue('id_cms') == 6) {
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
}
no need to add modules,
go to cms.tpl from your theme folder in prestashop,
add this
{if $cms->id==6}
{literal}
<script type="text/javascript" src="js/yourjsfile.js"></script>
{/literal}
{/if}
replace with your cms id and the name of your js file, then upload the file to js folder in prestahop root folder,
then go the prestahop panel, advanced parameters, performance, compile the templates and then launch your site --- the script will run only on the page selected
You can create a module and hook your js to backoffice header like this.
public function install()
{
if ( !$this->installTab()
|| !$this->registerHook('displayBackOfficeHeader'))
return false;
return true;
}
public function hookDisplayBackOfficeHeader()
{
//check if currently updatingcheck if module is currently processing update
if ($this->isUpdating() || !Module::isEnabled($this->name))
return false;
if (method_exists($this->context->controller, 'addJquery'))
{
$this->context->controller->addJquery();
$this->context->controller->addCss($this->_path.'views/css/gamification.css');
if (version_compare(_PS_VERSION_, '1.6.0', '>=') === TRUE)
$this->context->controller->addJs($this->_path.'views/js/gamification_bt.js');
else
$this->context->controller->addJs($this->_path.'views/js/gamification.js');
$this->context->controller->addJqueryPlugin('fancybox');
return $css_str.'<script>
var ids_ps_advice = new Array('.rtrim($js_str, ',').');
var admin_gamification_ajax_url = \''.$this->context->link->getAdminLink('AdminGamification').'\';
var current_id_tab = '.(int)$this->context->controller->id.';
</script>';
}
}
This a example show from prestashop core module gamification. After that you can write your own prestashop js code which you want.
In 2019 regarding PS 1.7 - we solved it here: https://www.prestashop.com/forums/topic/267834-how-to-insert-javascript-code-inside-a-page/
In short - add it directly to CMS content field with slight modifiations:
1) in class/Validation.php add
public static function isCleanHtmlWithScript($html, $allow_iframe = false)
{
$events = 'onmousedown|onmousemove|onmmouseup|onmouseover|onmouseout|onload|onunload|onfocus|onblur|onchange';
$events .= '|onsubmit|ondblclick|onclick|onkeydown|onkeyup|onkeypress|onmouseenter|onmouseleave|onerror|onselect|onreset|onabort|ondragdrop|onresize|onactivate|onafterprint|onmoveend';
$events .= '|onafterupdate|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditfocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onmove';
$events .= '|onbounce|oncellchange|oncontextmenu|oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondeactivate|ondrag|ondragend|ondragenter|onmousewheel';
$events .= '|ondragleave|ondragover|ondragstart|ondrop|onerrorupdate|onfilterchange|onfinish|onfocusin|onfocusout|onhashchange|onhelp|oninput|onlosecapture|onmessage|onmouseup|onmovestart';
$events .= '|onoffline|ononline|onpaste|onpropertychange|onreadystatechange|onresizeend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onsearch|onselectionchange';
$events .= '|onselectstart|onstart|onstop';
if (!$allow_iframe && preg_match('/<[\s]*(i?frame|form|input|embed|object)/ims', $html)) {
return false;
}
return true;
}
2) then in /classes/CMS.php around line #66 change
'content' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml', 'size' => 3999999999999)
to
'content' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtmlWithScripts', 'size' => 3999999999999)
now you should be good to go

Categories