Ajax function keeps messing up after inserting 24-25 records into the db - ajax

I have a multi select checkbox with 30-80 inputs, I wrote an Ajax code for this form, once checkbox is selected it sends records to the database.
Problem occurs after selecting 25th or 26th record, error is saying that value is duplicated, but every input's value is unique. and mentioned value in the error log is for the 1st selected checkbox.
What I want to achieve is, once a checkbox is clicked it should save it in the db, once the checkbox is unchecked, record should be removed from the database.
HTML:
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>
#foreach ($counties as $county)
<li>
<input class="checkIt" type="checkbox" id="cnty_id" name="cnty_id"
value="{{ $county->id }}" #foreach($jct_fr_cnty as $jct) #if($jct->cnty_id == $county->id) checked #endif #endforeach>
{{ $county->county }}
</li>
#endforeach
Ajax:
$('.checkIt').bind('click', function() {
if($(this).is(":checked")) {
console.log("Checked");
$.ajax({
type: "POST",
url: '{{ url('company/assignment/interstate/cntys') }}',
data: $("#cntyselectform").serialize()
}).done(function( result ) {
console.log(result);
});
} else {
console.log("Unchecked");
$.ajax({
type: "DELETE",
url: '{{ url('company/assignment/interstate/cntysrem') }}',
data: $("#cntyselectform").serialize()
})
}
});
Controller
public function postcntyinterstate(Request $request)
{
jct_fr_cnty::create([
'cmp_id' => $request->cmp_id,
'cnty_id' => $request->cnty_id,
'svc_id' => $request->svc_id
]);
return response()->json(['success'=>'Data is successfully added']);
}
this is a code for toggle all button, which checks all the checkboxes and sends values to the db without any issue.
function toggle(source) {
checkboxes = document.getElementsByName('cnty_id');
for (var i = 0, n = checkboxes.length; i < n; i++) {
checkboxes[i].checked = source.checked;
console.log("Checked");
$.ajax({
type: "POST",
url: '{{ url('company/assignment/interstate/cntys') }}',
data: $("#cntyselectform").serialize()
}).done(function(result) {
console.log(result);
});
}
}
form code:
<form id="cntyselectform" method="POST"
action="{{ route('company.cntyinterstate', ['company' => $company->id]) }}">
#csrf
<div class="col-lg-12 mb-3 mt-3 mb-sm-0">
<h3>Please select counties</h3>
<div name="cntSelect[]" id="cntSelect">
<ul>
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br />
#foreach ($counties as $county)
<li>
<input class="checkIt" type="checkbox" id="cnty_id" name="cnty_id"
value="{{ $county->id }}"
#foreach ($jct_fr_cnty as $jct) #if ($jct->cnty_id == $county->id) checked #endif #endforeach>
{{ $county->county }}
</li>
#endforeach
</ul>
</div>
<input type="hidden" value="{{ $company->id }}" name="cmp_id" id="cmp_id">
<input type="hidden" value="1" name="svc_id" id="svc_id">
{{-- <button id="cntyselect-button" type="submit" class="btn btn-primary">Submit</button> --}}
</div>
</form>
Database:

I understand the logic of. BUT it looks a little patchy. Let's fix it:
You absolutely should not have duplicate IDs. It may work, but it is semantically incorrect and you should not do it. (The id you specified in the loop in your code will be repeated in the document.) AND Get rid of unnecessary loops.
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>
#php
$jct_fr_cnty_array = array_column($jct_fr_cnty->toArray(), 'cnty_id');
#endphp
#foreach ($counties as $county)
<li>
<input class="checkIt" type="checkbox" name="cnty_id"
value="{{ $county->id }}" #checked(in_array($county->id, $jct_fr_cnty_array))>
{{ $county->county }}
</li>
#endforeach
A few elements that could not be marked and triggered incorrectly due to repeated ids in the document and their values may have caused this error.
If you perform your deletion operation properly and if you provide correct checking/unchecking conditions in the relevant checkboxes, I think the error will be corrected when you apply this.

Related

Laravel Livewire and select2 dropdown inside of bootstrap modal

I have a select2 blade component that I'm using inside of a bootstrap modal. Everything is working as expected, except when the modal form is submitted, the styling of the select 2 is lost, and a new empty option (must be the placeholder) is shown as an option, but only in the selects where I'm doing a yes/no option where the value is either 1 or 0 (See code below).
The styling is messed up on all, but the extra placeholder option only shows up on some.
Here's the normal way it should look, and does look when you first open the modal after a page refresh:
After submitting the modal it looks like this (notice the extra placeholder option):
Here's the code for my select2 component:
#props(['id', 'usePlaceholder' => null])
<div wire:ignore style="width:100%;" id="select2-{{ $id }}">
<select {{ $attributes }} id="{{ $id }}" name="{{ $id }}[]" class="form-select select2" data-width="100%" data-select2-id="{{ $id }}">
#if ($usePlaceholder)
<option></option>
#endif
{{ $slot }}
</select>
</div>
{{-- blade-formatter-disable --}}
#push('custom-scripts')
<script>
$(document).ready(function() {
let select2 = $('#' + #js($id));
select2.select2({
dropdownParent: $('#select2-' + #js($id)),
placeholder: 'Select an option',
minimumResultsForSearch: 15
});
***The below listeners I can comment out, they have no effect as far as I can tell, just including them for reference***
window.addEventListener('hideModal', event => {
select2.val(null).trigger('change');
});
window.addEventListener('set'+#js(ucfirst($id)), event => {
select2.val(event.detail).trigger('change');
});
});
</script>
#endpush
{{-- blade-formatter-enable --}}
And here's where I'm actually using the component:
<div class="col-md-6">
<x-input.group label="Sales Tax" for="salesTax" :error="$errors->first('salesTax')">
<x-input.select2 usePlaceholder="{{ empty($salesTax) ? 'true' : '' }}" wire:model.defer="salesTax" id="salesTax">
<option name="no" id="sales-tax-0" value="0">No</option>
<option name="yes" id="sales-tax-1" value="1">Yes</option>
</x-input.select2>
</x-input.group>
</div>
I've tried destroying and re-initializing the select2 when the modal is dismissed, but that hasn't done anything for me.

Livewire nestable is resetting order on drop

using the following library to add sorting and nesting https://www.hesamurai.com/nested-sort/latest, I'm also using the pre-rendered list option to populate the items (https://www.hesamurai.com/nested-sort/latest/pre-rendered).
Now my html structure looks like the following:
<div x-data class="pageContent__content__components mb10">
<span class="subTitle">Menu items</span>
<ol x-init="initSort($refs.draggable)" x-ref='draggable' id="draggable"
class='nested-sort nested-sort--enabled'>
#foreach($navigation->navigation_items as $item)
<li wire:key='{{ $item->id }}' draggable='true'
data-id="{{ $item->id }}"> {{ $item->navigatable->page_name ?? $item->navigatable->blog_title }}
<i wire:click="showAlert('{{ $item->id }}')"
class="fa-solid fa-trash-can"></i>
</li>
#if($item->children->count() > 0)
<ol data-id="{{ $item->id }}">
#foreach($item->children as $child)
<li draggable='true'
data-id="{{ $child->navigatable->id }}"> {{ $child->navigatable->page_name ?? $child->navigatable->blog_title }}
<i wire:click="showAlert('{{ $child->navigatable->id }}')"
class="fa-solid fa-trash-can"></i>
</li>
#endforeach
</ol>
#endif
#endforeach
</ol>
</div>
With the scripts being included at the bottom of the page
#push('scripts')
#once
<script src="https://cdn.jsdelivr.net/npm/nested-sort#5/dist/nested-sort.umd.min.js"></script>
<script type='text/javascript'>
function initSort(ref) {
return {
nested: new NestedSort({
el: ref,
actions: {
onDrop: function(data) {
#this.
call('saveMenuOrder', data);
},
},
}),
};
}
</script>
#endonce
#endpush
Now, adding and removing items to the list works as intended, but reordering or nesting any item causes livewire to reset the list to it's initial state.
If I where to add a wire:ignore to the <ol> that technically fixes the issue of livewire updating the DOM, but that also means that, when adding or removing items it no longer updates the list without manually refreshing the page.
My backend component looks like this:
// the model containing the items to be displayed (via a HasMany relation)
public NavigationModel $navigation;
// the method that is called everytime the onDrop action fires (the $data array contains the new order of elements)
public function saveMenuOrder(array $data): void
{
foreach ($data as $menuItem) {
$menuItemObject = $this->navigation->navigation_items->find(
$menuItem['id']
);
$menuItemObject->order = $menuItem['order'];
if (isset($menuItem['parent'])) {
$menuItemObject->parent_id = $menuItem['parent'];
} else {
$menuItemObject->parent_id = null;
}
$menuItemObject->save();
}
}
And that's basically it for the component, all I want is for livewire to update the list without messing up the DOM elements created by the library.
any ideas?
alpineJS is also installed, if that's a better solution that's also accepted.
thanks!
--- Edit
What I currenly have:
I converted the laravel foreach to an alpine x-for:
<div class="pageContent__content" style="grid-area: unset">
<div x-data='initSort($refs.draggable)'
wire:ignore
class="pageContent__content__components mb10">
<span class="subTitle">Menu items</span>
<ol class='nested-sort' x-ref='draggable' id="draggable">
<template x-for="item in items">
<li draggable="true" :data-id='item.id'>
<div class='nested-sort-item'>
<div class='nested-sort-item__text' x-text='item.text' />
<div class='nested-sort-item__actions'>
<button type='button' class='nested-sort-item__actions__button'
#click='$wire.showAlert(item.id)'
>
<i class='fas fa-trash-alt'></i>
</button>
</div>
</div>
</li>
</template>
</ol>
</div>
</div>
and rewrote the init function:
function initSort(ref) {
return {
navigation_items: #js($this->navigation_items),
get items() {
return this.navigation_items;
},
init() {
new NestedSort({
el: ref,
actions: {
onDrop: function(data) {
},
},
});
},
};
}
Now I can't seem to figure out how to access the navigation_items inside of my onDrop action, simply using this.navigation_items or this.items console.logs undefined.

Unable to validate form with single field, with ajax post

I've been unable to find a solution to my problem from searching. So I'd like to ask what might be wrong with my code. I'm trying to validate a form from forms.ModelForm but in my views function it won't pass the form.is_valid(). printing form.errors gives me:
<li>title<ul class="errorlist"><li>This field is required.</li></ul>
Model:
class Paper(models.Model):
title = models.CharField(max_length=100, help_text='Hello World!')
forms.FormModel
class eventCreateForm(forms.ModelForm):
class Meta:
Model = Paper
fields = ['title']
widgets = {
'title': forms.TextInput(attrs={'class' :'form-control', 'placeholder' : 'Place title'}),
}
Views
def create_paper(request):
context = {}
if request.method == 'POST':
form = paperCreateForm(request.POST or None, request.FILES or None)
if form.is_valid():
form_data = form.cleaned_data
t1 = form_data['title']
print(t1)
else:
context['create_paper_form'] = form
form_template = "user/paper-event-template.html"
return HttpResponse(render_to_string(form_template, {'context' : context}))
The form dosen't get validated, and in the else clause it'll pass the error when trying to retrieve it from the cleaned_data
I did try and print the form, and it shows:
<tr><th><label for="id_title">Title:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input class="form-control" id="id_title" maxlength="100" name="title" placeholder="Place Title" type="text" required /></td></tr>
But it dosen't contain any value, which I guess it should: I use a jax method for sending the forms data:
ajax
$('#create_paper_form_id').submit(function (event) {
event.preventDefault();
$.ajax({
type: "POST",
url: "{% url 'Eapp:create_paper' %}",
data: {
csrfmiddlewaretoken : '{{ csrf_token }}',
form_data : $('#create_paper_form_id').serializeArray(),
},
success: function (data) {
console.log(data);
$('.create-paper').html(data);
},
error: function() {
console.log('err');
}
});
});
html
<div class="create-paper">
<div class="container-fluid">
<form class="form" id="create_paper_form_id" novalidate="novalidate" action="{% url 'Eapp:create_event' %}" method="POST">
{% for field in create_paper_form %}
<div class="form-group">
<div class="col-xs-12">
{{ field.label_tag }}
</div>
<div class="col-xs-12">
{{ field }}
</div>
<div class="col-xs-6">
{{ field.help_text }}
</div>
<div class="col-xs-6">
{{ field.errors }}
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-xs-6 col-sm-6 col-md-2 col-lg-2">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</div>
That's expected behaviour, the cleaned_data is only available on a valid form when there's actually cleaned data available.
The form.data is still available though, you can use that.
To quote the manual:
A Form instance has an is_valid() method, which runs validation
routines for all its fields. When this method is called, if all fields
contain valid data, it will:
return True
place the form’s data in its cleaned_data attribute.
[edit] As #fazil-zaid mentioned, you need to include model in your Meta class for it to function as a modelform.

How to submit checked input?

I am using vuejs v.2, In my data structure I have products and keywords
they have many-to-many relationship. To attach keywords to product I have list of keyword's checkbox and when user submit only checked keyword should be attach to product
<div class="col-md-6" v-for="keyword in keywords">
<div class="checkbox">
<label>
<input type="checkbox" />
{{ keyword.name }}
</label>
</div>
</div>
Here I can not bind keyword.id as value (v-bind:value).
I just want to submit checked keyword ids to server
Please show me the correct way
I think the mistake you might be doing is not using v-model with an array data variable, following is working code:
vue component:
var demo = new Vue({
el: '#demo',
data: function(){
return {
keywords: [
{name: 'key1', id: 1 },
{name: 'key2', id: 2 },
{name: 'key3', id: 3 }
],
checked: []
};
}
})
and in HTML:
<div id="demo">
<div class="checkbox">
<label v-for="keyword in keywords">
<input type="checkbox" :id="keyword.name" v-bind:value="keyword.id" v-model="checked"/>
{{ keyword.name }}
<br>
</label>
<br>
checked value: {{checked}}
</div>
</div>
Working fiddle here

angularUI select dropdown displays data only after entering a character

I am using AngularJS v1.2.15 and angular-ui / ui-select. My select HTML is:
<div class="form-group col-md-3">
<div class="input-group select2-bootstrap-append">
<ui-select ng-model="modelOwner.selected" theme="select2" class="form-control">
<match placeholder="Select Owner">{{$select.selected.name}}</match>
<choices repeat="item in owner | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
</choices>
</ui-select>
<span class="input-group-btn">
<button ng-click="modelOwner.selected = undefined" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
My call in controller is:
$scope.modelOwner = {};
OwnersFactory.query({}, function (data) {
$scope.owner = data;
});
My service code:
bootstrapApp.factory('OwnersFactory', function ($http,$state,serviceUrl,$resource,$log) {
return $resource(serviceUrl + 'owner/:id', {}, {
show: { method: 'GET', params: {}, isArray: false }
})
});
Now, in my form i can view the values only after entering at least a single character. I want this select dropdown to display values just by clicking on the dropdown (not by entering any character.)
Possible Solution: if i could load my state only after all the AJAX calls have been made.
Please help me out here.
if you are using ui-router you can use resolve on each state, so that the call is resolved before initializing the controller
https://github.com/angular-ui/ui-router/wiki#resolve

Resources