Livewire and Flatpickr - fails after rerender - flatpickr

This may be a basic question, but I'm struggling. Essentially I have a livewire component that updates an array of flight information that a user enters. Whenever the components get rerendered, the flatpickr functionality stops working entirely. I presume this is because the javascript to initialize the component on that field is not running. What is the best practice to ensure these get rerendered with the appropriate javascript to enable the functionality.
Here's my blade snippet which renders fine on the initial load, but whenever a change to the data occurs, the page re-renders all the flights in the array, but the flatpickr functionality does not work anymore.
<form>
#foreach($flights as $i => $f)
<label
x-data
x-init="flatpickr($refs.input, {
dateFormat: 'Y-m-d H:i',
altInput: true,
altFormat: 'F j, Y h:i K',
enableTime: true,
})">
<div class="form-label">Arrival Time</div>
<div class="relative">
<input type="text"
wire:model="flights.{{ $i }}.ArrivalTime"
wire:key="fl{{ $i }}arrtime"
data-input
x-ref="input"
placeholder="Arrival Time"
value="{{ $f['ArrivalTime']}}"
name="flights[{{ $i }}][ArrivalTime]"
id="ArrivalTime{{$i}}"
/>
</div>
</label>
#endforeach
</form>
The livewire component is basically this:
class Itinerary extends Component
{
public $itin = null;
public $flights = [];
public function render()
{
return view('livewire.itinerary');
}
}

You need to wrap the input in a <div> like this:
<div wire:ignore>
<!-- Your input here -->
</div>
Source: https://laravel-livewire.com/docs/2.x/alpine-js#ignoring-dom-changes

Related

Checkbox value is refreshed after page reload in node JS

"I am creating TODO list using Node as backend. after adding every new item, a checkbox is also generating in front of them so I can apply "CSS line-through" to let user know that item is done or of no use. But when I add another item, the page refreshes and that checkbox is unchecked as I am not storing that value anywhere. Can you tell me how to store the value of that checkbox in the backend?
HTML -
<div class="box" >
<% for (var i=0; i<newListItems.length; i++) { %>
<div class="item">
<input type="checkbox" id="checkBox">
<p> <%= newListItems[i] %> </p>
</div>
<% } %>
<form action="/" method="post" class="item">
<input class="inputBox" type="text" name="newItem" placeholder="New item" autocomplete="off" required="required">
<button type="submit" name="list" value=<%= listTitle%>> +</button>
</form>
</div>
Node JS -
const items = [];
app.post("/", function(req, res){
let item = req.body.newItem;
items.push(item);
res.redirect("/");
});
The answer involves a lot of code, so I will give you a set of steps that can help in your case.
You need to change your data scheme. Currently looks like you are just storing the string in an array of items. You need to change it to be array of objects. Each object should have the field task and done. So you could know which task is done or not.
app.post("/", function(req, res) {
let item = req.body.newItem;
items.push({ name: item, done: false });
res.redirect("/");
});
Next step will be adding an endpoint that will be changing the done field of an array item to true.
Then on a front-end you will need to write some JS code that will be sending an HTTP request to the endpoint that marks the task as done. You need to use AJAX call for that, for example, NPM package axios.
Change the template to reflect the changes to the data. e.g. instead of <%= newListItems[i] %> do <%= newListItems[i].name %> and add logic to render checked checkbox based on done property.
It worth to mention, that you should not store data in memory, because once the process is done, you will lose your data. It is okay for learning purposes, but in production, you should use a database.

"Function Query.where() called with invalid data. Unsupported field value: undefined" Error in angular with firebase

I am using angular to create a booking web app using firebase. I am trying to query through the firebase data and filter them to avoid duplicate bookings to be recorded at the same time slots, but I keep getting the error Function Query.where() called with invalid data. Unsupported field value: undefined. I am new to angular and firebase and this is my first project as well. I could really use some help on this.
checkTimeSlot(){
this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot;
const queryTimeRefUFC = this.afs
.collection("ufc", ref => ref.where("date_time", "==", this.formData.date_time))
.get();
if (queryTimeRefUFC == null){
console.log("true");
return true;
}
else{
console.log("false");
return false;
}}
This is the method in the component.ts where I created the query search. This method is called when the value in the select input changes.
submitBookingUFC() {
this.formData.fullName = this.user.name;
this.formData.userID = this.user.id;
this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot;
if (this.checkTimeSlot() == true){
alert("The Time Slot you have selected has already been taken. Please select another.");
}
else if (this.checkTimeSlot() == false){
console.log(this.formData.date_time);
this.crudApi.SaveBookingUFC(this.formData);
alert("Record Saved Successfully");
this.ResetFormUFC(); // Reset form when clicked on reset button
} }
This is the method called when the user clicks the submit button.
<div>
<input
type="date"
class="in-bdate"
id="dateUFC"
name="dateUFC"
[min]="today"
max="{{bookMax}}"
value="{{formData.bookDate}}"
[(ngModel)]="formData.bookDate"
#dateUFC="ngModel"
required>
</div>
<div>
<div style="color:red"
*ngIf="dateUFC.errors && (dateUFC.dirty || dateUFC.touched)">
<p *ngIf="dateUFC.errors.required">
<sup>*</sup>Booking Date is required
</p>
</div>
</div>
<div *ngIf="!dateUFC.errors && (dateUFC.dirty || dateUFC.touched)">
<div>
<label for="timesUFC">Time Slot</label>
</div>
<div>
<select
name="timesUFC"
class="in-tslot"
value="{{formData.timeSlot}}"
(change)="checkTimeSlot()"
[(ngModel)]="formData.timeSlot"
#timeSlotUFC="ngModel"
required>
<option *ngFor="let time of timesUFC" >{{time.name}}</option>
</select>
</div>
<div>
<div style="color:red"
*ngIf="(timeSlotUFC.dirty || timeSlotUFC.touched)">
<p *ngIf="timeSlotUFC.errors && timeSlotUFC.errors.required">
<sup>*</sup>Time Slot is required
</p>
<p [ngModel]="timeError" name="error" ngDefaultControl>{{ timeError }}</p>
</div>
</div>
</div>
This is the html code I used to attain the date for the booking and the time of the booking. Using these I created a composite field as date_time using this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot; to avoid getting duplicate bookings for the same time slot on the same day. The formData here is linked to a class I created for the entire booking array using formData = new UfcData; where UfcData is the name of the class.
export class UfcData {
docID!: string;
userID!: string;
fullName!: string;
contactNo!: string;
timeSlot!: string;
bookDate!: string;
date_time!: string;}
This is the data in the UfcData class.
This is how my Collection and Documents look like
What I need is to acquire the user's input from the form which is acquire from ngModel to the UfcData class and make sure that there aren't any documents in the cloud firestore with the exact same date and time.
*After tweaking the code a little bit the above error does not display. However the if condition in the checkTimeSlot() method always returns true no matter what the option on the select is.
Thanks in advance.

Laravel Request does not return the modified request on validation fail

I have recently updated my controllers to use Requests to validate data before saving, originally I used $request->validate() within the controller route, but I am now at a stage where I really need to seperate it out in to a request.
Issue
Before validation takes place, I need to alter some of the parameters in the request, I found out this can be done using the prepareForValidation() method, and this works great, during validation the values in the request have been altered. My issue comes if the validation fails. I need to be able to return the request I've altered back to the view, at the moment, after redirection it appears to be using the request as it was before I ran prepareForValidation(). (i.e. returns title as 'ABCDEFG' instead of 'Changed The Title').
After some reading on other SO posts and Laravel forum posts, it looks as though I need to overwrite the FormRequest::failedValidation() method (which I've done, see code below), however I'm still struggling to find a way to pass my altered request back. I've tried to edit the failedValidation() method, I've provided details further down.
Expectation vs Reality
Expectation
User enters 'ABCDEFG' as the title and presses save.
The title is altered in the request (using prepareForValidation()) to be 'Changed The Title'.
Validation fails and the user is redirected back to the create page.
The contents of the title field is now `Changed The Title'.
Reality
User enters 'ABCDEFG' as the title and presses save.
The title is altered in the request (using prepareForValidation()) to be 'Changed The Title'.
Validation fails and the user is redirected back to the create page.
The contents of the title field shows `ABCDEFG'.
What I've tried
Passing the request over to the ValidationException class.
After digging through the code, it looks as though ValidationException allows a response to be passed over as a parameter.
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator, $this))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
However this results in the error Call to undefined method Symfony\Component\HttpFoundation\HeaderBag::setCookie() in Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::addCookieToResponse.
Flashing the request to the session
My next attempt was to just flash my request to the session, this doesn't seem to work, instead of my modified request being in the session, it looks to be the request before I ran prepareForValidation().
protected function failedValidation(Validator $validator)
{
$this->flash();
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
Returning a response instead of an exception
My final attempt to get this to work was to return a response using withInput() instead of the exception.
protected function failedValidation(Validator $validator)
{
return redirect($this->getRedirectUrl())
->withErrors($validator)
->withInput();
}
However it looks as though the code continues in to the BlogPostController::store() method instead of redirecting back to the view.
At this point I'm out of ideas, I just can't seem to get the altered request back to the view if validation fails!
Other Notes
I am pretty much a Laravel newbie, I have experience with a custom framework loosely based on Laravel, but this is my first CMS project.
I fully understand I may well be going down the wrong route, perhaps there's a better way of altering a request and passing it back when validation fails?
What am I trying to achieve by doing this? The main thing is the active checkbox. By default it is checked (See the blade below), if the user unchecks it and presses save, active is not passed over in the HTTP request, therefore active does not exist in the Laravel request object and when the user is returned back, the active checkbox has been checked again when it shouldn't be.
Then why have you used title in your example? I am using title in my post because I think it's easier to see what I am trying to achieve.
Any help is apreciated as I've currently burnt quite a few hours trying to solve this. 😣
Related Code
BlogPostController.php
<?php
namespace App\Http\Controllers;
use App\BlogPost;
use Illuminate\Http\Request;
use App\Http\Requests\StoreBlogPost;
class BlogPostController extends Controller
{
/**
* Run the auth middleware to make sure the user is authorised.
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('admin.blog-posts.create');
}
/**
* Store a newly created resource in storage.
*
* #param StoreBlogPost $request
* #return \Illuminate\Http\Response
*/
public function store(StoreBlogPost $request)
{
$blogPost = BlogPost::create($request->all());
// Deal with the listing image upload if we have one.
foreach ($request->input('listing_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('listing_image');
}
// Deal with the main image upload if we have one.
foreach ($request->input('main_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('main_image');
}
return redirect()->route('blog-posts.edit', $blogPost->id)
->with('success', 'The blog post was successfully created.');
}
}
// Removed unrelated controller methods.
StoreBlogPost.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class StoreBlogPost extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required',
'url' => 'required',
'description' => 'required',
'content' => 'required',
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'title.required' => 'The Title is required',
'url.required' => 'The URL is required',
'description.required' => 'The Description is required',
'content.required' => 'The Content is required',
];
}
/**
* Prepare the data for validation.
*
* #return void
*/
protected function prepareForValidation()
{
$this->merge([
'active' => $this->active ?? 0,
'title' => 'Changed The Title',
]);
}
/**
* #see FormRequest
*/
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
create.blade.php
#extends('admin.layouts.app')
#section('content')
<div class="edit">
<form action="{{ route('blog-posts.store') }}" method="POST" enctype="multipart/form-data">
#method('POST')
#csrf
<div class="container-fluid">
<div class="row menu-bar">
<div class="col">
<h1>Create a new Blog Post</h1>
</div>
<div class="col text-right">
<div class="btn-group" role="group" aria-label="Basic example">
<a href="{{ route('blog-posts.index') }}" class="btn btn-return">
<i class="fas fa-fw fa-chevron-left"></i>
Back
</a>
<button type="submit" class="btn btn-save">
<i class="fas fa-fw fa-save"></i>
Save
</button>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="form-group row">
<label for="content" class="col-12 col-xl-2 text-xl-right col-form-label">Active</label>
<div class="col-12 col-xl-10">
<div class="custom-control custom-switch active-switch">
<input type="checkbox" name="active" value="1" id="active" class="custom-control-input" {{ old('active', '1') ? 'checked' : '' }}>
<label class="custom-control-label" for="active"></label>
</div>
</div>
</div>
<div class="form-group row">
<label for="title" class="col-12 col-xl-2 text-xl-right col-form-label required">Title</label>
<div class="col-12 col-xl-10">
<input type="text" name="title" id="title" class="form-control" value="{{ old('title', '') }}">
</div>
</div>
</div>
</form>
</div>
#endsection
I had the same issue and here is the solution I found after digging through laravel code.
It seems that Laravel creates a different object for the FormRequest, so you can do something like this.
protected function failedValidation(Validator $validator)
{
// Merge the modified inputs to the global request.
request()->merge($this->input());
parent::failedValidation($validator);
}

geb cant find checkbox element

there is this piece of code that provides a checkbox following from a link to the T&C.
<div class="checkbox accept_agreement">
<label class="control-label" for="step_data_accept_agreement">
<input type="hidden" name="step_data[accept_agreement]" value="0">
<input type="checkbox" name="step_data[accept_agreement]" id="step_data_accept_agreement" value="1">
<label for="step_data_accept_agreement">
<a target="_blank" href="/lanevillkor/">
<font><font>I agree, have read and stored the terms and conditions</font></font>
</a>
</label>
</label>
</div>
Now, I am working on a spock test using geb, and I try to retrieve the checkbox element
<input type="checkbox" name="step_data[accept_agreement]" id="step_data_accept_agreement" value="1">
to do so i have tried many things without the expected output. i was expected that something like
$("#step_data_accept_agreement").click() would be pretty straight forward but it is not. in the other side if I put $("[for='step_data_accept_agreement'] label").click() it clicks the link.
I tried to become as more specific but nothing looks to return the element correctly.
one of my last attempts was
termsAndConditionsOption(wait: true) { $("#step_data_accept_agreement", name: "step_data[accept_agreement]") }
and the error message, as in the other cases too, was in one sentence
element not visible
What do I miss?
So the solution was to use js to click the checkbox. The simpliest way is:
def agreeOnTermsAndConditionsAccept() {
String mouseClickEvt = """
var evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
arguments[0].dispatchEvent(evt);
"""
browser.js.exec(termsAndConditionsOption.firstElement(), mouseClickEvt)
}
Geb provides js support to work with javascript, as we find in the documentation. Also another link shows how to simulate a click event with pure javascript.
This was required as the checkbox cant be found as it is hidden. With javascript we can 'see' the position of the element and perform an action in the location that we want. iniMouseEvent can be used as well as it is documentanted here

Global search box in angular

I want to implement a search box that changes what it searches based on whichever controller is being used. If you are on the "posts" view it will search the posts api, if you are on the videos view, it searches the videos api. It seems the search box would need its own controller maybe. I'm pretty sure I need to inject a search service into all the model controllers but I'm not exactly sure how to change the url it searches or tie the input to the different controller scopes.
So any ideas how to have a global search box that changes where it searches based on whichever controller is making use of it and tying its state back into a changing view?
To make a resource call dynamic api i would first create two $resources that map to your two endpoints, posts and videos. Then put an ng-change event on your global search that calls a function in your base controller.
This function firsts need to figure out what api to search. Then make the appropriate api call. The important part is in the callback and i think this is what you are looking for.
In the callback you could $broadcast the resp data from your api query. Each of your controllers will be listening for an event with an $on function. The listeners will then populate the correct scope variable with the callback data.
Pseudo below.
Same html layout with ng-change
<html>
<body ng-controller="AppController">
<form>
<label>Search</label>
<input ng-model="global.search" ng-change="apiSearch()" type="text" class="form-control" />
</form>
<div ui-view="posts">
<div ng-controller="PostController">
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
</div>
</div>
<div ui-view="videos">
<div ng-controller="VideoController">
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
</div>
</div>
</body>
</html>
AppController
.controller('AppController', function ($scope, PostService, VideoService) {
$scope.apiSearch = function() {
// Determine what service to use. Could look at the current url. Could set value on scope
// every time a controller is hit to know what your current controller is. If you need
// help with this part let me know.
var service = VideoService, eventName = 'video';
if ($rootScope.currentController == 'PostController') {
service = PostService;
eventName = 'post';
}
// Make call to service, service is either PostService or VideoService, based on your logic above.
// This is pseudo, i dont know what your call needs to look like.
service.query({query: $scope.global.search}, function(resp) {
// this is the callback you need to $broadcast the data to the child controllers
$scope.$broadcast(eventName, resp);
});
}
})
Each of your child controllers that display the results.
.controller('PostController', function($scope) {
// anytime an event is broadcasted with "post" as the key, $scope.posts will be populated with the
// callback response from your search api.
$scope.$on('post', function(event, data) {
$scope.posts = data;
});
})
.controller('VideoController', function($scope) {
$scope.$on('video', function(event, data) {
$scope.videos = data;
});
})
Client side filtering.
If you are not looking for anything to crazy that can be achieved in a super simple way for global search. I didnt even know if this would work so i just did a quick test and it does. Obviously this could be solved in a much more detailed and controlled way using services and injecting them where they are needed. But since i don't know excatly what you are looking for i will provide this solution, if you like it, great accept it. If you don't i could probably help you with service injection solution
Quick solution is to have an app wide contoller with $rootScope ng-model. Lets call it global.search.
$rootScope.global = {
search: ''
};
For the app wide search input.
<form>
<label>Search</label>
<input ng-model="global.search" type="text" class="form-control" />
</form>
In separate partials you just need to filter data based on the global.search ng-model. Two examples
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
Second template with different scope
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
Note how they both implement | filter: global.search. Whenever global.search changes, any filters in the current view will be changed. So posts will be filtered on the posts view, and videos on the videos view. While still using the same global.search ng-model.
I tested this, it does work. If you need more detail explaining the setup and child controller hierarchy let me know. Here is a quick look at a full template
<html>
<body ng-controller="AppController">
<form>
<label>Search</label>
<input ng-model="global.search" type="text" class="form-control" />
</form>
<div ui-view="posts">
<div ng-controller="PostController">
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
</div>
</div>
<div ui-view="videos">
<div ng-controller="VideoController">
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
</div>
</div>
</body>
</html>

Resources