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

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.

Related

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);
}

Livewire and Flatpickr - fails after rerender

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

How to get and post 2 objects from ng-model?

I have here a sample document from mongodb named chapters:
{_id:5c014c999cc48c3b0057988b, chapter_name:"AB"}
Controller: here is my function to get the chapters, and function to post to another db:
.controller('regCtrl', function($http, $location, $timeout, User, Bloodbankchapter) {
Bloodbankchapter.getBloodbankchapters().then(function(data) {
app.bloodbankchapters = data.data.bloodbankchapters;
});
this.regUser = function(regData) {
User.create(app.regData).then(function(data) {});
};
Then in my front-end to register:
<form name="regForm" ng-submit="register.regUser(regData); ">
<label>Chapter Name</label> <!--After I select the chapter name -->
<select ng-model="register.regData.branch" ng-options ="chapter._id as chapter.chapter_name for chapter in register.bloodbankchapters">
<option ng-repeat="chapter in register.bloodbankchapters" >{{chapter.chapter_name}}</option>
</select>
<label>Chapter ID</label> <!--chapter ID will appear here-->
<input type="text" ng-model="register.regData.branch_id" hidden>
<label>Chapter name</label> <!--How to make chapter name appear here?-->
<input type="text" ng-model="register.regData.branch_name" hidden>
<button ng-disabled="register.disabled" type="submit">Submit</button>
</form>
My problem here is after I selected the chapter name the second input was correct and it post the id into the db, but how can I get the chapter.name and post it to db?
I tried getting the text value from the select box and then appending it to a textbox like here: http://jsfiddle.net/kxqJN/, but when I register, the object id is being registered instead of chapter_name like this:
`{branch_id:5c014c999cc48c3b0057988b, branch_name:"5c014c999cc48c3b0057988b"}`
How can I display the name in the first box and the id in the second?
I bet you haven't tried what i suggested in the comments: chapter.chapter_name for chapter in register.bloodbankchapters track by chapter._id will lead you to have both values in your model:
angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.register = {
regData: {
branch: {},
},
bloodbankchapters: [
{_id:'5c014c999cc48c3b0057988b', chapter_name:"AB"},
{_id:'5c014c999cc48c3b0057988c', chapter_name:"A"},
],
};
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="selectExample" ng-controller="ExampleController">
<select ng-model="register.regData.branch" ng-options ="chapter.chapter_name for chapter in register.bloodbankchapters track by chapter._id">
<option ng-repeat="chapter in register.bloodbankchapters" >{{chapter.chapter_name}}</option>
</select>
<div>{{register.regData.branch}}</div>
</div>
Try this way, you will get selected chapter object inside register.regData.branch
<select ng-model="register.regData.branch" ng-options ="chapter as chapter.value for chapter in register.bloodbankchapters">

Node.js with express insert new field into existing mongoDB document from server side

Hello I am looking to implement a way to dynamically insert new fields to an existing mongoDB document from the server side with node.js and express.
For example in the local mongoDB the document looks like this.
{
value: 'Google',
url: 'https://google.com',
env: 'Test'
}
I have a route that will already update the current document fields from a form on the UI. However I want to combine that logic with the ability to insert new fields upon updating.
The route below handles updating the document with the existing fields.
router.put("/:id", (req, res) => {
let value = req.body.value;
Application.findByIdAndUpdate(req.params.id, req.body.application, (err,
updatedApp) => {
if(err){
console.log(err);
} else {
console.log(updatedApp)
req.flash("info", updatedApp.value + " " + "successfully edited!");
res.redirect("/qa-hub/applicationmanager");
}
});
});
On the front end I use EJS with a form to update the document. Example below:
<div class="row">
<div class="col-md-6">
<div class="form-group">
<input class="form-control" type="url" name="application[url]" value="<%= application.url %>" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<select class="form-control" name="application[env]" required="true">
<option class="text-center" value='<%= application.env %>'><%= application.env %></option>
<option value='Beta'>Beta</option>
<option value='Dev'>Dev</option>
<option value='Does Not Apply'>Does Not Apply</option>
<option value='Prod'>Prod</option>
<option value='QA'>QA</option>
<option value="Test">Test</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<a class="btn btn-outline-warning" href="/qa-hub/applicationmanager">Cancel</a>
</div>
<div class="form-group">
<button class="btn btn-outline-primary" id="btn" >Update</button>
However i'd like to add three additional fields upon submitting the form. I want to capture the currently logged in user that performed the edit and the date and time. I already have that worked out but how could I implement inserting new data to the existing document from the route.put while also keeping the logic to update the current fields if any changes are made.
So after the user makes some changes and updates the three fields the document would look something like below, except id handle the logic to get the currently logged in user at that time and the date/time and pass it in but for the example below I will hardcode it.:
{
value: 'Google',
url: 'https://google.com',
env: 'Test',
updatedBy: "Test User"
timeUpdated: "12:54",
dateUpdated: "7/25/2018"
}
So ultimately I want to keep a log of the changes and than be able to add it to the UI.
So with a little help from this post TypeError: callback.apply is not a function (Node.js & Mongodb) I was able to append new fields to the existing document using $set. However when trying to perform the req.body.application before $set it would throw an error stating that callback.apply is not a function. So I just created a callback if you will to update the document after setting the new fields. I know its messy but just wanted to get it working feel free to use and clean up the code for your self.
router.put("/:id", (req, res) => {
let value = req.body.value;
let value = req.body.value;
let date = new Date();
let hour = date.getHours();
hour = (hour < 10 ? "0" : "") + hour;
let min = date.getMinutes();
min = (min < 10 ? "0" : "") + min;
let time = hour+":"+min;
let year = date.getFullYear();
let month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
let day = date.getDate();
day = (day < 10 ? "0" : "") + day;
today = month+"/"+day+"/"+year;
let updatedTo = month+"/"+day+"/"+year;
let updatedT = hour+":"+min;
let updatedBy = req.user.username;
//Find the document based on it's ID and than append these three new fields
Application.findByIdAndUpdate(req.params.id,
{ $set: {
updatedTime: `${updatedT}`,
updatedToday: `${updatedTo}`,
updatedBy: `${updatedBy}`
}}, { upsert: true },
(err,updatedApp) => {
if(err){
return handleError(err);
} else {
// Than if any changes were made from the UI we apply those updates taken
// from the form with req.body.application
Application.findByIdAndUpdate(req.params.id, req.body.application,
(err, updatedApp) => {
if(err){
return handleError(err);
} else {
console.log(updatedApp)
req.flash("info", updatedApp.value + " " + "successfully edited!");
res.redirect("/qa-hub/applicationmanager");
}
}
});
});

Jhipster : disable dialog close on submit

I am using Jhipster V3.
I have created an entity, and Jhipster generated all the needed views.
My client wants that in the update dialog of this entity, the click on Save doesn't close the popup.
I have commented one line on this function :
var onSaveSuccess = function (result) {
$scope.$emit('rhTechvalleyApp:consultantUpdate', result);
//$uibModalInstance.close(result);
vm.isSaving = false;
};
In this popup I have a datepicker component. Its value is getting blank once the save process has finished. It s only a display issue, but I don't know how to get rid of it.
If anyone knows...
Thanks.
[UPDATE]
transformResponse: function (data) {
data = angular.fromJson(data);
data.dateEnregistrement = DateUtils.convertLocalDateFromServer(data.dateEnregistrement);
data.dateDernierPointDisponibilite = DateUtils.convertLocalDateFromServer(data.dateDernierPointDisponibilite);
data.dateDisponibilite = DateUtils.convertLocalDateFromServer(data.dateDisponibilite);
return data;
}
<div class="input-group">
<input id="field_dateEnregistrement" type="text" class="form-control" name="dateEnregistrement" uib-datepicker-popup="{{dateformat}}" ng-model="vm.consultant.dateEnregistrement" is-open="vm.datePickerOpenStatus.dateEnregistrement"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="vm.openCalendar('dateEnregistrement')"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
You need to add transformResponse to the update function of the Angular service for your entity. It should be the same as transformResponse for get.
Example:
'update': {
method:'PUT',
transformResponse: function (data) {
data = angular.fromJson(data);
data.birthDate = DateUtils.convertLocalDateFromServer(data.birthDate);
return data;
}
}
The reason you need to do this is because the server will return a string like 2016-04-12 for dates. This needs to be converted to a JavaScript Date object, which is what Angular expects for type="date" inputs. If you use a string instead of a date object for ngModel on a date input, Angular will throw an error and fail to fill the field. You can use DateUtils.convertLocalDateFromServer for LocalDate and DateUtils.convertDateTimeFromServer for DateTime.

Resources