How do I properly handle submit events in Vue + Rails? - ruby-on-rails

I'm learning Rails, Vue and JS and I've got a question on the proper handling of submit buttons on Rails using Vue component to validate it. I'm using mdbootstrap for the styles.
I built a form wizard which uses vee-validate for validating the fields and in some forms I want to perform some server side operations too (eg.: validate exact address with geocoding). I'm currently facing basically three issues.
Although I added a v-clock directive, I'm still seeing a little
flicker every time the form wizard component gets loaded (eg.: page
refresh).
I had to workaround the Rails automatic data-disable-with handling to get it working, and it looks not optimal to me, and I'd like to know if there's a better way to deal with it (I had to disable the submit event propagation and prevent default and do the disabling/enabling manually otherwise the handler from Rails UJS will receive it afterwards and disable the button forever).
Although the button gets enabled again, it gets brighter every time I click on it if validation fails (some handler from mdbootstrap maybe?). It happens only after I click on refresh button on the browser and I've noticed the following div is created after each click followed by an error during form validation, causing the button to become "brighter" as in a accumulated "disabled effect":
Anyone has ideas on how these issues could be solved? Thanks!
new.html.erb:
<div id="stepper">
<div v-cloak>
<transition-group name="fade">
<div class="d-none d-lg-block" key="progress_bar">
<%= render 'forms/progress_bar' %>
</div>
<div id="step1" v-if="step === 1" key="step1">
<%= render 'forms/description' %>
</div>
<div id="step2" v-if="step === 2" key="step2">
<%= render 'forms/address' %>
</div>
</transition-group>
</div>
</div>
_description.html.erb:
<template>
<form
id="description-form"
data-vv-scope="description-form"
novalidate="true"
#submit.prevent="next('description-form', $event)">
<div class="row mb-5">
<div class="col-lg-12 col-md-12">
<div class="container">
<div class="row" id="step-1">
<div class="col-lg-6">
<div class="max-height-80">
<div class="mb-4">
<h4><%= t(:'step1.title') %></h4>
</div>
<div class="form-group">
<label
for="name"
class="control-label">
<%= t(:'step1.label.name') %>
</label>
<input
id="name"
name="name"
type="text"
class="form-control"
placeholder="<%= t(:'step1.input.name') %>"
v-validate="'required'"
v-model="name"
:class="{ 'is-invalid': errors.has('name','description-form') }"
required/>
<div
v-if="errors.has('name','description-form')"
class="invalid-feedback">
<%= t(:'name.required') %>
</div>
</div>
<div class="form-group">
<label
for="description"
class="control-label">
<%= t(:'step1.label.description') %>
</label>
<textarea
id="description"
name="description"
class="form-control"
placeholder="<%= t(:'step1.input.description') %>"
rows="11"
v-validate="'required'"
v-model="description"
:class="{ 'is-invalid': errors.has('description','description-form') }"
required>
</textarea>
<div
v-if="errors.has('description','description-form')"
class="invalid-feedback">
<%= t(:'description.required') %>
</div>
</div>
</div>
<footer class="page-footer white fixed-bottom d-block d-sm-none z-depth-1" id="footer">
<div class="d-flex justify-content-end">
<button class="btn btn-default pull-right" type="submit" data-remote="true" data-disable-with="<%= wait_spinner %>"><%= t(:'btn.next') %></button>
</div>
</footer>
<div class="d-none d-sm-block">
<div class="d-flex justify-content-end mt-2">
<button class="btn btn-default pull-right" type="submit" data-remote="true" data-disable-with="<%= wait_spinner %>"><%= t(:'btn.next') %></button>
</div>
</div>
</div>
<div class="col-lg-2"></div>
<div class="col-lg-4 d-none d-lg-block mt-lg-5">
</div>
</div>
</div>
</div>
</div>
</form>
</template>
stepper.js
var element = document.getElementById("stepper");
if (element != null) {
Vue.use(VeeValidate);
Vue.use(VueResource);
Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const stepper = new Vue({
el: element,
data() {
return {
/**
* The step number (starting from 1).
* #type {Integer}
*/
step:1,
name:null,
description:null
}
}
},
methods: {
/**
* Goes back to previous stepp
*/
prev() {
this.step--;
},
/**
* Triggers validation of current step and goes to the
* next step if validation succeeds
* #param {String} scope The step scope used for validation.
* #param {Object} Event that triggered the next step (form submit)
*/
next(scope, event) {
if (event != undefined) {
event.stopImmediatePropagation();
event.preventDefault();
event.stopPropagation();
}
const form = event.currentTarget;
$("button[type=submit]",form).each(function() {
Rails.disableElement(this);
});
this.validateFields(scope, event);
},
validateFields(scope, event) {
this.$validator.validateAll(scope).then(function (valid) {
this.postFieldsValidation(valid, event);
}.bind(this));
},
postFieldsValidation(valid, event) {
if (valid) {
stepper.step++;
}
const form = event.currentTarget;
$("button[type=submit]",form).each(function() {
Rails.enableElement(this);
});
},
handleError(error) {
alert(error)
},
submit() {
}
}
});
}
* edit *
for #3, I'm using a workaround for removing the div with waves-ripple class inside my button when re-enabling elements on the form.
$("div").remove("button .waves-ripple");
however, it would be nice to know the root cause for it.

Related

Angular 7 Reactive Form returns previous entered value on Pressing Enter key

I have created a login form using angular reactive form. I've facing some strange behavior
If I submit the form using the submit button, I get the latest values which I've entered. If I submit the form using Enter key, then the value received is not latest. I've added the code below, can anyone pls suggest what i'm doing wrong.
ngOnInit() {
this.loginForm = this.formBuilder.group(
{
username: new FormControl(),
password: new FormControl(),
RememberMe: []
},
{
updateOn: 'blur'
}
);
}
login() {
const formvalue = this.loginForm.value;
console.log(formvalue);
return;
}
<div class="limiter">
<div class="container-login100 img-bg">
<div class="wrap-login100">
<img class="logo-center" src="../../../../assets/images/logo-big.png" />
<form [formGroup]="loginForm">
<div class="ui segment">
<h2>Login Form</h2>
<div class="ui form" >
<div class="required inline field">
<label>Username</label>
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" name="Username" placeholder="username" formControlName="username" >
</div>
</div>
<div class="required inline field">
<label>Password</label>
<div class="ui left icon input">
<i class="lock icon"></i>
<input [type]= "'password'" placeholder="password" formControlName="password" >
</div>
</div>
<div class="field login-ckeck">
<sui-checkbox>
Remember me
</sui-checkbox>
</div>
<button [ngClass]= "'ui primary button'" (click)= "login()">Submit</button> Forgot your password?
</div>
</div>
</form>
</div>
<!-- <footer class="footer">footer</footer> -->
</div>
</div>
Found the solution, it was an error from my side.
I had to change the updateOn to submit, for my code to work
this.loginForm = this.formBuilder.group(
{
username: new FormControl(),
password: new FormControl(),
RememberMe: []
},
{
updateOn: 'submit'
}
);
You can listen to either the forms (ngSubmit) or directly to the (keyup.enter) event and trigger the event then.
<form (ngSubmit)="login()">
<form (keyup.enter)="login()">

How to add multiple fields in bootbox.prompt in mvc

I want to add a project name and task name while clicking on a particular date in fullcalendar but I don't know how to use bootbox.prompt or bootbox.dialog with more than one fields so can you help me out?
select: function (start, end, allDay) {
debugger;
bootbox.prompt("Add New Event", function (title) {
debugger;
if (title !== null) {
calendar.fullCalendar('renderEvent',
{
title: title,
start: start,
end: end,
allDay: allDay,
className: 'label-info'
},
true // make the event "stick"
);
}
});
It's quite simple, we can use bootbox dialog for that
bootbox.dialog({
title: 'Add New Event',
message: $('#form'),
show: false,
}).on("shown.bs.modal", function (e) {
$('#form').show()
}).on('hide.bs.modal', function (e) {
/**
* Bootbox will remove the modal (including the body which contains the login form)
* after hiding the modal
* Therefor, we need to backup the form
*/
$('#form').hide().appendTo('body');
})
.modal('show');
calendar.fullCalendar('unselect');
}
In html
<form id="form" method="post" class="form-horizontal" style="display: none;">
<div class="form-group">
<label class="col-xs-3 control-label">Username</label>
<div class="col-xs-5">
<input type="text" class="form-control" name="username" />
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Password</label>
<div class="col-xs-5">
<input type="password" class="form-control" name="password" />
</div>
</div>
<div class="form-group">
<div class="col-xs-5 col-xs-offset-3">
<button type="submit" class="btn btn-primary" style="float:right;">Login</button>
</div>
</div>

Phonegap how to auto show keyboard

I have a phonegap application using Angular. The problem I'm having is that I want to allow user to enter their email in a form inside a modal.
$scope.email = function () {
$uibModal.open({
animation: true,
templateUrl: 'views/modals/sales/email.html',
size: "md",
windowClass: 'center-modal',
controller: function ($scope, $uibModalInstance) {
//$("#email").focus();
$scope.submit = function () {
};
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
}
}
});
};
Now the template for that modal is this.
<div class="panel panel-primary">
<form name="form" ng-submit="submit(form)" novalidate>
<div class="panel-body">
<div class="col-md-12">
<h3 class="text-center">Please enter your email</h3>
</div>
<div class="form-group col-md-12">
<input type="email" class="form-control btn-block input-lg" ng-model="customer.email" id="email" autofocus>
</div>
<div class="col-md-3 col-md-offset-6">
<button class="btn btn-default btn-block btn-lg" type="button" ng-click="close()">Close</button>
</div>
<div class="col-md-3">
<button class="btn btn-primary btn-block btn-lg" type="submit" ng-click="submit()">Send</button>
</div>
</div>
</form>
</div>
The problem that as you can see the input type email need to be focus when the modals open up, but instead my input is getting focus but the virtual keyboard on iOS is not showing up until I manually tap on the input.
How can I show up the virtual keyboard when the modal opens up.
thanks.
install the below cordova plugin
https://github.com/driftyco/ionic-plugin-keyboard
call cordova.plugins.Keyboard.show()

ReactJS.NET : Unable to get click events to fire

I'm running ReactJS.NET with ASP.NET MVC, and everything renders well, except... i cannot get any kind of events to fire...
What i have tried :
1) Eliminating all JQuery references/usages - no success
2) onHover, onClick, with functions both inside and outside of the React-component - no success
Here is my code :
Attaching ProductLine React component in index.cshtml
#Html.React("ProductLine", Model)
React component
function addToCart() {
alert("Hoohohoho!!!");
};
var ProductLine = React.createClass({
render: function () {
return(
<div className="col-md-12 col-sm-12 col-xs-12" key={this.props.productData.name}>
<button onClick={this.addToCart}>Click me!</button>
<div className="row" >
<div onClick={addToCart} className="col-md-12 col-sm-12 col-xs-12 nopadding row-default" >
<div className="col-md-3 col-sm-5 col-xs-5 nopadding">
<span className="table table-col ">
<input type="checkbox" id="cbxCheck2" className="jq_select_product" />
<label htmlFor="cbxCheck2" className="jq_select_product">{ this.props.productData.name }</label>
</span>
</div>
<div className="col-md-4 hidden-sm hidden-xs nopadding">
<span className="table table-col table-text-small">
{ this.props.productData.label }
</span>
</div>
<div className="col-md-3 col-sm-4 col-xs-4 nopadding">
<span className="table table-col">
849,- (12 mnd)
</span>
</div>
<div className="col-md-2 col-sm-3 col-xs-3 nopadding">
<span className="table table-col table-text-small text-right">
<img id="imgShowMore" src="../../Images/arrow_purple_down.png" className="show-more _icon jq_expand_listview" /><label className="show-more _lbl jq_expand_listview">Vis mer</label>
</span>
</div>
</div>
<ProductDetails productData={this.props.productData} />
</div>
</div>
);
},
addToCart: function (e) {
alert("Hooooo!!!");
},
});
After 1/2 a day of trial and error, i suddenly tried to put all the JSX file references at the bottom of the _Layout.cshtml page...
That solved it all actually...

Form validations lost when included in template

I have a directive, which successfully includes a template in my AngularJS app using Rails 4.2 and angular-rails-templates.
Everything seems to be working fine, even the two way data binding, although there is one exception. Form validation using AngularJS is lost if I include the <form> tag inside the template file.
When I run this in my local environment, Angular includes the template, display it on my main page. if you enter data the two way data binding updates, but the form validation is not handled by Angular, it uses HTML5 browser validation instead.
Any help appreciated, I assume there is some common thing I am missing, like how / when the template is inserted?
I have prepared a working demo at Plnkr, the same code executes without incident on Plnkr, but not when using Rails.
JS:
var app = angular.module('app', ['templates']);
app.config([
'$httpProvider', function($httpProvider) {
return $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
app.run(function() {
return console.log('angular app running');
});
app.directive('serviceRequest', function() {
return {
restrict: 'E',
templateUrl: 'service-request.html',
controller:function() {
this.request = {};
this.addService = function(request) {
//$http request
this.request = {};
}
},
controllerAs: 'service'
};
});
The HTML template (service-request.html):
<form name="myForm" novalidate="">
<div>
serviceForm is {{myForm.$valid}}
</div>
<fieldset>
<legend>Service Request</legend>
<h1>{{service.request.number}} <small>{{service.request.reported | date}}</small></h1>
<div class="row">
<div class="small-12 medium-10 large-10 small-centered">
<div class="row">
<div class="small-3 columns">
<label for="right-label" class="right">Number:</label>
</div>
<div class="small-9 columns">
<label>
<input type="text" ng-model="service.request.number" placeholder="small-9.columns" required/>
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="small-12 medium-10 large-10 small-centered">
<div class="row">
<div class="small-3 columns">
<label for="right-label" class="right">Reported:</label>
</div>
<div class="small-9 columns">
<input type="text" ng-model="service.request.reported" placeholder="small-9.columns" required/>
</div>
</div>
</div>
</div>
<input type="submit" ng-disabled="myForm.$invalid ">
</fieldset>
</form>

Resources