For some reason, I can't quite figure out how to get an 'autocomplete' style multiselect with Vue working properly.
I properly set the route that is being called in my axios block, and the controller is set to use the query as a way to hit the database with a LIKE clause, but something is going wrong somewhere and my multiselect is not being filled with results from database that would make it searchable.
What am I doing wrong here?
Route:
Route::get('search', 'Controller#searchTags')
->name('search');
Controller:
public function searchTags(Request $request)
{
if ($request->get('query')) {
$query = $request->get('query');
$data = TAGS::where('TAG_DATA', 'LIKE', "%{$query}%")->get();
$output = '<ul class="dropdown-menu" style="display:block; position:relative">';
foreach ($data as $row) {
$output .= '<li>' . $row->tag_data . '</li>';
}
$output .= '</ul>';
return $output;
}
}
Blade:
<div id="tagContent">
<multiselect v-model="value" open-direction="bottom" :options="options" :multiple="true" :close-on-select="false" :taggable="true" :clear-on-select="false" :preserve-search="true" placeholder="Add Tag(s)" label="name" track-by="name">
<template slot="selection" slot-scope="{ values, search, isOpen }"><span class="multiselect__single" v-if="values.length && !isOpen">{{ values.length }} options selected</span></template>
</multiselect>
</div>
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data () {
return {
value: [],
options: []
}
},
methods: {
read(){
window.axios.get('campaigns/search').then(({ data }) =>{
console.log(data)
});
},
addTag (newTag) {
const tag = {
name: newTag,
code: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.options.push(tag)
this.value.push(tag)
}
}
}).$mount('#tagContent');
There are a few things missing from your example, I believe.
You need to trigger the read function when the search input changes - use the #search-change event for that
You need to make use of the results of your axios request by sending them to this.options, so that the multiselect component can make use of them.
In this example, I've mocked the data request using a timeout, but you should get the idea. You can also make use of the loading property to let your users know something is happening behind the scenes.
Related
I am trying to make a form that changes based on user response, I have a ChoiceType::class for the first part with 'yes' or 'no' as the options. If the user selects 'yes' I want the second part of the form to show up to get their response to that, but if they select 'no' I just want to keep that second form hidden.
This is the form I have so far
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('attending', ChoiceType::class, [
'choices' => [
'yes' => true,
'no' => false,
],
'attr' => [
'class' => 'attendanceStatus'
],
'mapped' => false,
'required' => true,
'label' => 'Will you be attending?',
'placeholder' => 'Please make selection',
])
->add('bringingGuest', ChoiceType::class, [
'choices' => [
'yes' => true,
'no' => false,
],
I wrapped the forms in a class and gave each form an ID
<div class="attendance">
<div id="attendance-status">
{{ form_label(form.attending) }}
{{ form_errors(form.attending) }}
{{ form_widget(form.attending) }}
</div>
<div id="guest" style="display: none;">
{{ form_label(form.bringingGuest) }}
{{ form_errors(form.bringingGuest) }}
{{ form_widget(form.bringingGuest) }}
</div>
</div>
I'm not the greatest with javascript but I tried to do an if statement like this
if ('.attending' == true) {
document.getElementById('guest').style.display = 'block';
}
I've been at this for a bit of time now and I can't seem to figure out how to do it properly. I thought it would be something like having an event listener for the user selection and then just using javascript to show the second form if conditions are met.
This is, like you probably already know, a javascript question. Like you said, you can use an event listener, for example to run a function whenever a value changes. Here's an example:
const someId = document.getElementById('some-id');
const example = document.getElementById('example');
someId.addEventListener('change', doSomething);
function doSomething() {
if (someId.value === "yes") {
example.innerHTML = "YES!"
} else {
example.innerHTML = ""
}
}
<select name="name" id="some-id">
<option value="no">no</option>
<option value="yes">yes</option>
</select>
<div id="example">
</div>
All you need to do is check what IDs you should target and what you want to happen on which event. To find out more about events, see: https://developer.mozilla.org/en-US/docs/Web/Events.
You may want to check How to Dynamically Modify Forms Using Form Events page in Symfony docs.
In your case it will be Dynamic Generation for Submitted Forms section.
It involves 2 types of events - Form events on PHP/Symfony side and JS mostly "passive" role to listen for element change and replace the HTML.
The idea is next:
On user action on the HTML element send a request to backend and render the new state for the page based on the element's changed value. Here you get full page HTML as a response.
Then just replace dependent chunks of the page with the new rendered pieces of HTML you get from the response.
Here are main parts (copy-pasted from the docs):
// src/Form/Type/SportMeetupType.php
namespace App\Form\Type;
use App\Entity\Position;
use App\Entity\Sport;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormInterface;
// ...
class SportMeetupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('sport', EntityType::class, [
'class' => Sport::class,
'placeholder' => '',
])
;
$formModifier = function (FormInterface $form, Sport $sport = null) {
$positions = null === $sport ? [] : $sport->getAvailablePositions();
$form->add('position', EntityType::class, [
'class' => Position::class,
'placeholder' => '',
'choices' => $positions,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
$formModifier($event->getForm(), $data->getSport());
}
);
$builder->get('sport')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$sport = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $sport);
}
);
}
// ...
}
in JS:
{# templates/meetup/create.html.twig #}
{{ form_start(form) }}
{{ form_row(form.sport) }} {# <select id="meetup_sport" ... #}
{{ form_row(form.position) }} {# <select id="meetup_position" ... #}
{# ... #}
{{ form_end(form) }}
<script>
var $sport = $('#meetup_sport');
// When sport gets selected ...
$sport.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$sport.attr('name')] = $sport.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current position field ...
$('#meetup_position').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#meetup_position')
);
// Position field now displays the appropriate positions.
}
});
});
</script>
I am trying to update view_count column on every #click. but couldn't figure out the right way of it.
First made the controller --resources and fetch datas via api.
controller:
public function index()
{
$articles = Article::all();
return response()->json([
"articles" => $articles
], 200);
}
public function show($id)
{
$article = Article::whereId($id)->first();
return response()->json([
"article" => $article
], 200);
}
also set the update function too.
public function update(Request $request, $id)
{
$view = Article::find($id);
$view->update($request->where("view_count"));
return response()->json(["message" => "view_count updated"]);
}
I set the api routes:
Route::get('/articles', 'ArticlesController#index');
Route::get('/articles/{id}', 'ArticlesController#show');
Route::get('/articles/{id}', 'ArticlesController#update');
And finally in Vue.js
<p class="button">
<i #click.prevent="count" class="fas fa-chevron-right"></i>
</p>
data(){
return {
view: 0,
};
},
methods: {
count: function(){
axios.post("/api/articles" + this.item.id).then(response => {
this.view++;
})
window.location.href = "pages/" + this.item.id
}
}
it's counting but not update the col. also, when I refresh the page of course it will start to count from 0... it's not really efficient way to it. what is the best and right way to do it?
Thank you.
Not: By the way I am fetching and iterating api in the parent component:
<div class="listWrap" :key="item.id" v-for="item in filterArticles">
<list :item="item" />
</div>
your workflow to update views is wrong.
first, we should change our uri method of the update method to GET like below :
Route::get('/articles/update/{id}', 'ArticlesController#update');
then, our update method within ArticlesController to increment view_count value:
public function update(int $id)
{
// i have changed the $view by $article
$article = Article::find($id);
$article->view_count++;
$article->save();
return response()->json(["message" => "view_count updated", 201]);
}
and within our Vue component, we should update the URI of the update method and the HTTP method name because we should use the same HTTP verb in both client and server sides.
<p class="button">
<i #click.prevent="count" class="fas fa-chevron-right"></i>
</p>
<script>
export default {
// as you are using parent/child relationship betwen components, you should use props.
props: { item: Object },
data(){
return {
view: 0,
};
},
methods: {
count: function(){
axios.get(`/api/articles/update/${this.item.id}`).then(response => {
this.view++;
})
window.location.href = "pages/" + this.item.id;
}
}
}
</script>
I had the need to load a Gravity Form through Ajax on a push of a button (in PHP/Wordpress), and thanks to Steven Henty, I've found his solution to fix my issue. Modified it at little to open my form in a modal (lity modal), and it works!
However.... Now I started to migrate my site to the new Gutenberg editor in Wordpress. So I need a system that can do the same from a Gutenberg block (javascript) code.
I can code some, but I am not hardcore, so would love if anyone could tell me how to implement this former (PHP based) 'system', for instance to a (javascript based) Gutenberg Button-block, that implements this code. Here is the code for the current (working) button:
button.php
// Hook up the AJAX ajctions
add_action('wp_ajax_nopriv_gf_button_get_form', 'gf_button_ajax_get_form');
add_action('wp_ajax_gf_button_get_form', 'gf_button_ajax_get_form');
// Add the "button" action to the gravityforms shortcode
// e.g. [gravityforms action="button" id=1 text="button text"]
add_filter('gform_shortcode_button', 'gf_button_shortcode', 10, 3);
function gf_button_shortcode($shortcode_string, $attributes, $content)
{
$a = shortcode_atts(array(
'id' => 0,
'text' => 'Open',
'button_class' => '',
'button_style' => ''
), $attributes);
$form_id = absint($a['id']);
$curr_lang = ICL_LANGUAGE_CODE;
if ($form_id < 1) {
return '<div>Missing the ID attribute.</div>';
}
gravity_form_enqueue_scripts($form_id, true);
$ajax_url = admin_url('admin-ajax.php');
$html = sprintf('<button id="gf_button_get_form_%d" class="gf_button_get_form %s" style="%s"><div class="gf_button_get_form-label">%s</div></button>', $form_id, $a['button_class'], $a['button_style'], $form_id, $a['text']);
$html .= "<script>
(function (SHFormLoader, $) {
$('#gf_button_get_form_{$form_id}').click(function(){
$.ajaxSetup({
beforeSend: function() {
$('.spinner_{$form_id}').addClass('active');
},
complete: function() {
$('.spinner_{$form_id}').removeClass('active');
var fieldsWithHiddenLabels = $('.gfield.hidden-label');
if (fieldsWithHiddenLabels.length) {
fieldsWithHiddenLabels.each(function(){
if($(this).hasClass('gfield_contains_required')){
$(this).find('.ginput_container label').prepend('<span class=\"gfield_required\">*</span>');
}
});
}
}
});
$.get('{$ajax_url}?lang={$curr_lang}&action=gf_button_get_form&form_id={$form_id}',function(response){
lity(response);
if(window['gformInitDatepicker']) {gformInitDatepicker();}
});
});
}(window.SHFormLoader = window.SHFormLoader || {}, jQuery));
</script>";
return $html;
}
function gf_button_ajax_get_form() {
$form_id = isset($_GET['form_id']) ? absint($_GET['form_id']) : 0;
gravity_form($form_id, true, false, false, false, true);
die();
}
I am using the excellent create-guten-blocks as boilerplate, and my template block file looks like this:
form-button.js
import './style.scss';
import './editor.scss';
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
registerBlockType( 'my-blocks/form-button', {
title: __( 'Form Button' ),
icon: heart,
category: 'common',
keywords: [
__( 'my-blocks — Form Button' )
],
edit: function( props ) {
return (
<div className={ props.className }>
<p>— Hello from the backend.</p>
<p>
CGB BLOCK: <code>configit-blocks</code> is a new Gutenberg block
</p>
<p>
It was created via{ ' ' }
<code>
<a href="https://github.com/ahmadawais/create-guten-block">
create-guten-block
</a>
</code>.
</p>
</div>
);
},
save: function( props ) {
return (
<div>
<p>— Hello from the frontend.</p>
<p>
CGB BLOCK: <code>configit-blocks</code> is a new Gutenberg block.
</p>
<p>
It was created via{ ' ' }
<code>
<a href="https://github.com/ahmadawais/create-guten-block">
create-guten-block
</a>
</code>.
</p>
</div>
);
},
} );
Hope this makes sense. This is my second post on Stackoverflow, so please let me know if you need more details ... Really hope one of you skilled people can handle this. Thanks!
I have my back-end setup like this.
public function index()
{
$doc = Document::all();
return response()->json([
'Success' => [
'documents' => $doc->toArray()
],
], 200);
// return $doc;
}
In the front-end I have my factory class like this
query:
{
method:'GET',
isArray:false
},
and controller like this.
DocumentsFactory.query(function(documents) {
$scope.documents = documents;
});
Now this setup runs just fine. Here is my console.log
When I try the following in my html.
<div class="col-sm-5">
<select class="form-control m-b-sm" id="procedureId" ng-change="getSelectedProcedure(selectedProcedure.item)" ng-model="selectedProcedure.item" ng-options="document as (document.id + '-' + document.name) for document in documents" required="true"/>
<option value=" ">----------</option>
</select>
</div>
It does not work. Simply shows nothing in selection. Am I doing things the wrong way? What is the best practice for this kind of thing?
I have a EXTjs grid, which displays some fields from my custome tables in database..
Reports.grid.Reports = function(config) {
config = config || {};
Ext.applyIf(config,{
id: 'reports-grid-reports'
,url: Reports.config.connectorUrl
,baseParams: { action: 'mgr/reports/getList' }
,save_action: 'mgr/reports/updateFromGrid'
,fields: ['id','name','filename','remark','year','resourceID','typeID','visible']
and then :
{
header: 'Type ID'
,dataIndex: 'typeID'
,sortable: true
,width: 100
}
My types table has typeid and name.. my grid displays the number, How can I tell it to display the coresponding name for that id in the table instead?
I also got a combobox which I use in update window:
Reports.combo.Types = function(config) {
config = config || {};
Ext.applyIf(config,{
name: 'typeID'
,hiddenName: 'typeID'
,displayField: 'name'
,valueField: 'id'
,fields: ['name','id']
,listWidth: 380
,pageSize: 20
,url: Reports.config.connectorUrl
,baseParams: {
action: 'mgr/reports/gettypelist2', 'combo': true
}
});
Reports.combo.Types.superclass.constructor.call(this,config);
};
Ext.extend(Reports.combo.Types,MODx.combo.ComboBox);
Ext.reg('combo-modx-types',Reports.combo.Types);
Using ,displayField: 'name' makes combo display name instead of the id.. how can I do the same thing in grid?
If I understand the question correctly, I believe you would need to customise the getList processor rather than in the ExtJS directly. Override the afterIteration() function. Something along the lines of:
public function afterIteration(array $list) {
$rows = array();
foreach ($list as $row){
// This calls a custom getName() function that gets the name from the other table
$row['typeID'] = $this->getName($row['id']);
$rows[] = $row;
}
return $rows;
}
and maybe your getName() function could be something like:
private function getName($id) {
$otherTable = $this->modx->getObject('TABLE_ALIAS_HERE', array('internalKey' => $id));
$name = $otherTable->get('name');
return $name;
}