I am trying to activate or deactivate the products for a form using $product->status
The active button shows if $product->status is 0 and
The deactive button shows if $product->status is 1
I want to toggle the value of $product->status in the mysql database every time I click on the button
<form action="{{route('invetory.create')}}" method="POST">
#csrf
<table class="table table-bordered" id="dynamicTable">
<tr>
<th>item</th>
<th>tax</th>
<th>Action</th>
</tr>
#forelse($products as $product)
<input type="text" name="item" value="{{$product->id}}
class="form-control" hidden />
<td>
<input type="text" name="item" value="{{$product->item}}
class="form-control" />
</td>
<td>
<input type="text" name="tax" value="{{$product->tax}}
class="form-control" />
</td>
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
</table>
</form>
here i have given the route i have used
web.php
Route::post('/update', 'App\Http\Controllers\InventoryController#update')>name('invetory.update');
here i have added the controler i have used
InventoryController.php
public function update(Request $REQUEST){
dd($REQUEST->all());
Inventory::update( $REQUEST->invetory as $key => $value);
return redirect()->action([InventoryController::class, 'index']);
}
i am geting 500 error when i click button
You can achieve this using POST request which will refresh the page each time you toggle a product or you can use AJAX to do the change asynchronously. Using Javascript and AJAX would be the preferred way so you don't lose selected filters, pagination etc.
You don't need external packages to implement that, you can use JavaScript's fetch method. Also, instead of having 2 separate functions and routes, I would suggest having one route that would toggle the product's status, i.e. if it is active, make it inactive and vice versa. That method by definition should be a POST request, by I prefer doing GET requests for this in order to avoid CSRF protection and use middleware to protect the request.
Here is the complete code.
Register a web route that toggles the state inside web.php
Route::get('projects/toggle', [ProjectController::class, 'toggle'])->name('projects.toggle');
Implement the toggle method in ProjectController.php
public function toggle(Request $request) {
$project = Project::find($request->project_id);
$project->status = !$project->status;
$project->save();
return response()->json(['status' => (int) $project->status]);
}
Notice that I am returning a json response which includes the new project status. We will use this in the JavaScript code to dinamically update the column where the status is shown.
Finally, in the blade file, when iterating through the projects, the button click calls a function that will do the AJAX request. Notice that I am also adding an id attribute to the columns that contains the status so I can access it dinamically in order to update it.
#foreach($projects as $project)
<tr>
<td>{{$project->title}}</td>
<td id="project-status-{{$project->id}}">{{$project->status}}</td>
<td><button onClick="toggleStatus('{{$project->id}}')">Toggle</button></td>
</tr>
#endforeach
In this same file, we add the following JavaScript code. It accepts project_id as parameter which is passed from the button click, makes the ajax request to backend which updates the status and then updates the appropriate DOM element to show the new status.
function toggleStatus(project_id) {
fetch("{{ route('projects.toggle') }}?project_id=" + project_id)
.then(response => response.json())
.then(response => {
document.querySelector("#project-status-" + project_id).innerHTML = response.status;
})
}
As I mentioned, you can use multiple options in the JavaScript part. Instead of calling a function you can register an event listener to each button, but this approach with function call is a bit quicker. Also, I am passing the project_id as GET parameter, you can define the route to contain it as route parameter, but then you'll need to do some string replacements in order to do in dinamically in JavaScript. All in all, the proposed is a good solution that will serve your purpose.
p.s. For stuff like this, LiveWire is a perfect fit.
Using dd (e.g. in your controller) will throw a 500 error. It literally stands for "dump and die".
check you routes in form use{{route('invetory.create')}}
and in routes you given inventory.update
public function Stauts(Request $request)
{
$product= Product::findOrFail($request->id);
$product->active == 1 ? $product->active = 0 : $product->active = 1 ;
$product->update();
return response()->json(['status' => true,'msg' => 'Staut updated']);
}
in blade use ajax
<script>
$(document).on('click', '.status-product', function (e) {
e.preventDefault();
var product_id = $(this).data('id');
var url ="{{ route('product.status') }}";
$.ajax({
type: "post",
url: url,
data: {
'_token': "{{csrf_token()}}",
'id': product_id
},
success: function (data) {
if (data.status == true) {
$('#deactive_ajax').show();}
}
})
})
</script>
Route::post('product/stauts/', [productController::class,'Stauts'])->name('product.Stauts');
First of all
Using a form with tables is not ideal and some browsers already made changes to prevent that.
Secondly
The best way is as DCodeMania said, the ajax request is the best way to solve this, I'll just modify his answer a bit and use Patch instead of PUT, so it'll look like this:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PATCH',
data: {
id: id,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
and you'll only be needing one button so no need to make the check for $product->status he added, just a single button for the toggle will make your code cleaner.
As for using PATCH instead of PUT, because you're only updating one single column and not the whole thing getting updated, and no need for the status parameter, you'll just reverse what's inside the database
$product = Product::find($request->id);
Product::where('id', $product->id)->update([
'status' => $product->status ? 0 : 1,
]);
You'll also need one button with different text based on status
like this
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">{{ $product->status == 1 ? 'deactivate' : 'activate' }}</button>
</td>
I did it this way and it works for me
First in view does this
<td>
#if ($producto->estado == '1')
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-success" id="btn_estado">Activo</a>
#else
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-danger" id='btn_estado'>Inactivo</a>
#endif
</td>
Controller
function updateStatusProducto($id)
{
//get producto status with the help of producto ID
$producto = DB::table('productos')
->select('estado')
->where('id', '=', $id,)
->first();
//Check producto status
if ($producto->estado == '1') {
$estado = '0';
} else {
$estado = '1';
}
//update producto status
$values = array('estado' => $estado);
DB::table('productos')->where('id', $id)->update($values);
return redirect()->route('productos.index');
}
Route
Route::get('/status-update-producto/{id}', [ProductoController::class, 'updateStatusProducto']);
You could add some data attributes to your buttons, and use them in js to send an ajax request
#if ($product->status =='0')
<td>
<button type="button" class="btn btn-success toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">active</button>
</td>
#else
<td>
<button type="button" class="btn btn-danger toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">Deactive</button>
</td>
#endif
document.querySelectorAll('.toggle-tr').forEach(el => el.addEventListener('click', e => {
const product = e.target.dataset.product;
const status = e.target.dataset.status == 0 ? 1 : 0;
// send ajax or fetch request passing in product_id. If we're going with a RESTful approach,
axiox.patch(`/products/${product}`, { status })
.then(res => { ... })
.catch(err => { ...});
}));
You can use jQuery ajax here:
Pass product id in data-id attribute
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
then use ajax:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 1,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
$(document).on('click', '.deactive_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 0,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
Now you can handle the request in the controller.
i've to make a query base on two dates, if the dates exists, if not just run the query without any filtration, but i dont know how send the dates input value from client side to server side , here is my views
def priceByDateTime(request):
start = request.GET.get('from')
end = request.GET.get('to')
print(start,end)#
if start and end:
datetimes = MyModel.objects.filter(invoice__created_at__range=(start,end)).annotate(
total_price=Sum(
(F('price')) - F('discount'),output_field=DecimalField(max_digits=20,decimal_places=3))
).annotate(
total_quantity=(
Count('pk')
)
).aggregate(
all_price=Sum(F('total_price')),
all_qnt=Sum(F('total_quantity'))
)
else:
datetimes = MyModel.objects.all().annotate(
total_price=Sum(
(F('price')) - F('discount'),output_field=DecimalField(max_digits=20,decimal_places=3))
).annotate(
total_quantity=(
Count('pk')
)
).aggregate(
all_price=Sum(F('total_price')),
all_qnt=Sum(F('total_quantity'))
)
return JsonResponse(datetimes,safe=False)
#login_required
def queryTemplate(request):
return render(request,'myapp/datetimes.html')
i know how to make the query dont sure how send the input date type values to backend
and here is my GET form, to get the two date from
$(document).ready(function(){
const start_date = new Date($('#from').val());
const end_date = new Date($('#to').val());
console.log(start_date)
console.log(end_date)
if(start_date && end_date){
data={
'from':start_date,
'to':end_date
}
}
function dateTimePrices(){
$.ajax({
type:'GET',
url:'/prices/dateTime/data',
data:data,
success:function(data){
const datetimes = data;
var k = '<tbody>';
if(datetimes){
k+= '<tr>';
k+= '<td>' + datetimes["all_qnt"] + '</td>';
k+= '<td>' + datetimes['all_price'] + '</td>';
k+= '</tr>'
}else{
k+= '<td class="p-2 text-xs border border-purple-900 md:text-base textpurple" colspan=6>found nothing</td>'
}
k+='</tbody>'
document.getElementById('datetime_list').innerHTML = k
}
})
}
dateTimePrices();
})
<form action="" method="GET">
<div class="col-11 p-1 mt-1 mx-auto text-center row rtl ">
<p class="col-12 col-sm-6 mx-auto text-left row">
from
<input type="date" class="form-control col-9 mr-1" name="from" id="from">
</p>
<p class="col-12 col-sm-6 mx-auto text-right row">
to
<input type="date" name="to" class="form-control col-9 mr-1" id="to">
</p>
<button class="btn btn-info col-8 col-sm-5 col-md-3 mx-auto">search</button>
</div>
</form>
in the console shows invalid date and in the backend shows :
django.core.exceptions.ValidationError: ['“Invalid Date” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']
i much appreciate any idea, Thank you in advance ...
i also have to check if input dates exists then call the data variable into the ajax, but now even if i didnt any search still pretend like the date exists and return this error from server side
you need to add an event handler for the form submission using jquery!
$( "#target" ).submit(function( event ) {
alert( "Handler for .submit() called." );
event.preventDefault();
});
I need to Auto-fill up the crispy fields so I called the needed data from my database using ajax function as:
views.py
def load_record(request):
PUITS_id = request.GET.get('PUITS')
record = SurveillanceDesPuits.objects.filter(PUITS_id__id__exact=PUITS_id)[:1]
my_record= [str(record[0].PUITS) , str(record[0].MODE), str(record[0].CS)]
print(my_record)
return render(request, 'measure/Surveill_Wells/Add_wellMntr.html', {'record': my_record})
And my HTML file is :
<form method="POST" id="SurveillanceDesPuits_F" data-record-url="{% url 'ajax_load_record' %}">
{% csrf_token %}
<!-- form from views.py-->
<div class="border p-2 mb-3 mt-3 border-secondary">
<div class="form-row">
<div class="form-group col-md-3 mb-0">
{{ form.PUITS|as_crispy_field }}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.CS|as_crispy_field }}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.MODE|as_crispy_field }}
</div>
</div>
</div>
<input class="btn btn-success mb-4" type="submit" value="ADD Record">
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_PUITS").change(function () {
var url = $("#SurveillanceDesPuits_F").attr("data-record-url");
var PUITSId = $(this).val();
$.ajax({
url: url,
data: {
'PUITS': PUITSId
},
success: function (data) {
$("#id_MODE").html(data);
}
});
});
</script>
After selecting an item (PUITS) from the dropdown list, I want to set the value of CS and MODE automatically from the received data.
so in the console, it gives me this error:
File "D:\WikiPED\venv\lib\site-packages\crispy_forms\templatetags\crispy_forms_filters.py", line 102, in as_crispy_field
raise CrispyError("|as_crispy_field got passed an invalid or inexistent field")
crispy_forms.exceptions.CrispyError: |as_crispy_field got passed an invalid or inexistent field
[07/Sep/2021 17:30:05] "GET /ajax/load-record/?PUITS=1 HTTP/1.1" 500 25693
what I missed in this code?
Thanks
I changed the views.py as:
def load_record(request):
PUITS_id = request.GET.get('PUITS')
record = SurveillanceDesPuits.objects.filter(PUITS_id__id__exact=PUITS_id)[:1]
return JsonResponse({'record2': list(record2.values())}, safe=False)
the scripts will be:
<script type="text/javascript">
$.ajax({
type: 'GET' ,
url: url,
data: {'PUITS': PUITSId },
dataType: "json",
success: function (response){
const object = response.record2[0]
$("#id_PUITS").val(object.PUITS_id);
$("#id_DATE_TEST").val(object.DATE_TEST);
$("#id_MODE").val(object.MODE);
$("#id_CS").val(object.CS);
$("#id_SITUATION").val(object.SITUATION);
$("#id_DUSE").val(object.DUSE);
$("#id_PRES_TBG").val(object.PRES_TBG);
$("#id_PRES_CSG").val(object.PRES_CSG);
$("#id_PRES_AVD").val(object.PRES_AVD);
$("#id_RESEAU_GL").val(object.RESEAU_GL);
$("#id_ANNULAIRE_TECH").val(object.ANNULAIRE_TECH);
$("#id_OBSERVATION").val(object.OBSERVATION);
$("#id_Controle_Pression_ENSP").val(object.Controle_Pression_ENSP);
$("#id_Test_Puits").val(object.Test_Puits);
$("#id_Controle_Pression_DP").val(object.Controle_Pression_DP);
},
});
return false;
});
</script>
I get 403 error due to csrf_token verification failure in spite of token explicit declaration in an ajax call. I for my data dictionary in the same manner in other functions and it works just great.
Here is JS:
$(document).on("click", ".update-cleaning", function(event){
event.preventDefault();
var input_field_name = $(this).attr('name')
var obj_id = $(this).attr('value')
var model_name = $(this).attr('data-model')
var field_name = $(this).attr('data-field')
var value = $("#" + input_field_name + obj_id).val();
if (!value) {
alert('Fill the field!')
}
else {
$.ajax({
url: "{% url 'ajax_update_cleaning' %}",
type: "POST",
data: {'object_id': obj_id, 'model_name': model_name, 'field_name': field_name, 'value': value, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
})
.done(function(response){
console.log(response);
})
}
});
My html form is in a popover which is toggled by a click on <td> and looks like this:
<td class="text-md-center with-popover" name="frequency" value="{{cleaning.id}}">
{{ cleaning.frequency }}
<div id="frequency{{cleaning.id}}" style="display: none">
<form method="POST">
<label for="cleaning_frequency">Frequency: </label>
<input id="cleaning_frequency{{cleaning.id}}" type="number" name="cleaning" value="{{cleaning.frequency}}">
<button type="submit" class="btn btn-success btn-sm update-cleaning" name="cleaning_frequency" data-model="Cleaning" data-field="frequency" value="{{cleaning.id}}"> Change </button>
</form>
</div>
</td>
Could you give me some ideas? Thank you.
In any template that uses a POST form, use the csrf_token tag inside the element if the form is for an internal URL, e.g.:
<form method="post">{% csrf_token %}
Read more in the Django Documentation
When I click on continue button it checks clients ID in firestore database and if ID does'nt exist then $scope.filePath = "createClient.htm" is called. everything is working fine but when I call this piece of code inside firebase function nothing happens
Inside HTML
<div ng-include="filePath" class="ng-scope">
<div class="offset-1 offset-sm-2 col-11 col-sm-7">
<h2>Enter Client ID</h2>
<br>
<div class="form-group">
<label for="exampleInputEmail1">ID</label>
<input id="client_id" type="text" class="form-control" placeholder="Client ID">
</div>
<div class="float-right">
<button type="button" class="btn btn-danger">Cancel</button>
<button type="button" ng-click="searchClient()" class="btn btn-success">Continue</button>
</div>
</div>
</div>
Internal Script
<script>
$scope.searchClient = function()
{
//$scope.filePath = "createClient.htm"; it works here
var id= document.getElementById('client_id').value;
db.collection("clients") // db is firestore reference
.where("c_id","==",id)
.get()
.then(function(querySnapshot)
{
querySnapshot.forEach(function(doc)
{
if(!doc.exists)
{
console.log("Client Does'nt Exist");
$scope.filePath = "createClient.htm"; // but doesnt works here inside this firebase function
}
}
);
}
);
}
};
</script>
when console.log is shown and firebase function are fully executed then if I click on "Continue" Button again it works fine and content of createClient.htm is shown inside ng-include="filePath". why i need to click twice ?