call async function from angular controller - angular-dart

I want to call an async function from angular form template controller. But if I do so, execution hangs.
Following the html template:
<div class="container">
<form>
<div class="form-group">
<label for="address">Identnummer (Adresse) *</label>
<input type="text" class="form-control" id="address" required placeholder="0x"
[(ngModel)]="model.address"
#address="ngForm"
[ngClass]="setAddressCssValidityClass(address)"
ngControl="address">
</div>
<div class="row">
<div class="col-auto">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<small class="col text-right">* Required</small>
</div>
</form>
</div>
Here is a simplified version of my controller:
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
#Component(
selector: 'sCert-validate-form',
templateUrl: 'sCert_validate_form_component.html',
directives: [coreDirectives, formDirectives],
)
class ScertValidateFormComponent {
Map<String, bool> setAddressCssValidityClass(NgControl control) {
var validityClass;
//callCertificateByAddress(control.value);
if(control.value == "test"){
validityClass = 'is-valid';
callCertificateByAddress(control.value);
}
else{
validityClass = 'is-invalid';
}
Map<String, bool> map = {validityClass: true};
return map;
}
Future<bool> callCertificateByAddress(String address) async {
// some code -> also does not work if removed
return true;
}
}
Future<bool> callCertificateByAddress(String address) async has to be async, because I do async lib calls in there. But how can I achieve this in combination with the template?

there is a problem: you bind callback to the input here [ngClass]="setAddressCssValidityClass(address)" angular calls your function [which calls network] on each change detection cycle. and the response from http forces a new change detection which recursively calls http again and again. save the result to some variable and bind it to ngClass instead of a method
ngOnInit() {
this.classes = this.setAddressCssValidityClass(address);
}
....
[ngClass]="classes"

just use async pipe like this
[ngClass]="setAddressCssValidityClass(address) | async"

Related

How to implement validation rules for elements from a partial view

We are developing a .net core 3.1 MVC application (actual with MVVMC).
We have implemented custom validation attributes. And want them to be checked on server and on client side.
The view is a standard view, however the user has the possibility to add multiple partial views to the standard view (via a button).
In the partial view we were not able to use the 'asp-for' tag helper within the input fields, because one can have several times the same item added, and we need to be able to create a list in the ViewModel out of those additional partial views selected. That's why we tried to build the tags by ourselfes.
Once a partial view has been requested via ajax we are calling the validator again. Server validation works, client validation only works for those inputs, which are on the main view, not for the inputs on the partial views. However, after refreshing or when the server sends the user back after a validation error, client validation starts working (because site is completely refreshed and jquery validation takes input fields of partial views into account).
Please find the code below:
Implementation of validation attribute
public class AllowedExtensionsAttribute: ValidationAttribute, IClientModelValidator
{
private readonly List<string> _extensions = new List<string>();
public AllowedExtensionsAttribute(string extensions)
{
_extensions.Add(extensions);
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is IFormFile file)
{
var extension = Path.GetExtension(file.FileName);
if (!_extensions.Contains(extension.ToLower()))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-extension", ErrorMessage);
}
}
MainViewModel
[AllowedExtensions(".pdf", ErrorMessage = "This data type is not allowed!")]
public IFormFile PdfDocumentOne { get; set; }
PartialViewModel
[AllowedExtensions(".pdf", ErrorMessage = "This data type is not allowed!")]
public IFormFile PdfDocumentTwo { get; set; }
Main View
<form method="post" asp-controller="Home" asp-action="MainView" enctype="multipart/form-data">
<div class="form-group top-buffer">
<div class="row">
<div class="col-2">
<label asp-for="PdfDocumentOne" class="control-label"></label>
</div>
<div class="col-3">
<input asp-for="PdfDocumentOne" class="form-control-file" accept="application/pdf" />
<span asp-validation-for="PdfDocumentOne" class="text-danger"></span>
</div>
</div>
</div>
<div id="container">
<div id="containerFull" class="form-group">
<div class="row">
<div class="col-2">
<label class="control-label">...</label>
</div>
<div class="col-10">
<div id="containerPartialView">
</div>
</div>
</div>
<div class="row">
<div class="col-2">
</div>
<div class="col-3">
<button id="AddPartialView" type="button" class="form-control">...</button>
</div>
</div>
</div>
</div>
...
<div class="form-group top-buffer">
<div class="row">
<div class="col-2">
<input type="submit" value="Submit" class="form-control" id="checkBtn" />
</div>
</div>
</div>
</form>
Partial View
<input id="Lists[#Model.ViewId].ViewId" name="Lists[#Model.ViewId].ViewId" class="partialViewModel" type="hidden" value="#Model.ViewId" />
<div id="Lists[#Model.ViewId].MainContainer" class="partialView">
<div class="form-group">
<div>
<div class="col-2">
<label asp-for="PdfDocumentTwo" class="control-label"></label>
</div>
<div class="col-3">
<input name="Lists[#Model.ViewId].PdfDocumentTwo" id="Lists[#Model.ViewId].PdfDocumentTwo " type="file" class="form-control-file" accept="application/pdf"
data-val="true" data-val-extension="This data type is not allowed!"/>
<span class="text-danger field-validation-valid" data-valmsg-for="Lists[#Model.ViewId].PdfDocumentTwo" data-valmsg-replace="true"></span>
</div>
</div>
</div>
...
</div>
Javascript
function AddPartialView() {
var i = $(".partialView").length;
$.ajax({
url: '/Home/AddPartialView?index=' + i,
success: function (data) {
$('#containerPartialView').append(data);
$().rules('remove','extension');
jQuery.validator.unobtrusive.adapters.addBool("extension");
},
error: function (a, b, c) {
console.log(a, b, c);
}
});
}
$('#AddPartialView').click(function () {
AddPartialView();
});
jQuery.validator.addMethod("extension",
function (value, element, param) {
var extension = value.split('.').pop().toLowerCase();
if ($.inArray(extension, ['pdf']) == -1) {
return false;
}
return true;
});
jQuery.validator.unobtrusive.adapters.addBool("extension");
HomeController
[HttpPost]
public IActionResult MainView(MainViewModel vm)
{
if (vm == null)
{
return RedirectToAction("Index");
}
DatabaseHelper.GetMainViewModel(vm);
if (!ModelState.IsValid)
{
#ViewData["StatusMessageNegative"] = "The entered data is not valid. Please scroll down to correct your data.";
return View(vm);
}
return RedirectToAction("UploadDocument", new { Id = vm.Id});
}
[HttpGet]
public ActionResult AddPartialView(int index)
{
PartialViewModel pvm = DatabaseHelper.GetPartialViewModel(index);
return PartialView("Partial", pvm);
}
After searching in the internet we found the following argument (data-ajax="true"). However, we don't know where to place it or how to use it correctly.
Have you tried re-apply validation after loading your partial view?
$.ajax({
url: '/Home/AddPartialView?index=' + i,
success: function (data) {
$('#containerPartialView').append(data);
$().rules('remove','extension');
jQuery.validator.unobtrusive.adapters.addBool("extension");
},
error: function (a, b, c) {
console.log(a, b, c);
}
}).then(function () {
$("form").each(function () { $.data($(this)[0], 'validator', false); });
$.validator.unobtrusive.parse("form");
});

angular 5 form with form builder

I am trying to implement a form in Angular5. But there occurs a error in the console which states 'formGroup needs to have an instance of FormGroup'
This is the HTML code:
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()" novalidate>
<div class="input-section">
<div class="form-group filled-form">
<label for="businessName">What positions are you hiring for?</label>
<input class="form-control myInputStyle" type="text" id="enterEmail"
formControlName="businessname" required>
<div [hidden]="userInfo.controls['businessname'].valid ||
userInfo.controls['businessname'].pristine" class="inline-error">
Please enter a Business name!
</div>
</div>
</div>
</form>
Here is the ts code :
export class HwaPreviewComponent implements OnInit {
public userInfo:FormGroup;
constructor(private router: Router) { }
ngOnInit() {
}
onSubmit() {
if (this.userInfo.valid) {
console.log("Form Submitted!");
}
}
public userInfo:FormGroup; - adding this in the .ts file helped

Angular 4 form validation

How to validate a form in angular 4 by clicking on external link (ie out side from tag). If the form is valid do some actions with form data else show validation messages. If form is valid I don't want to submit the form just need to get the form field values.
This is my answer post to another question:
The easy way is to use reactive forms, like this:
Code:
import {ReactiveForm, FormBuilder, Validators} from '#angular/form';
export class SignupFormComponent implements OnInit {
userForm: FormGroup;
firstName: string;
constructor(private _formBuilder:FormBuilder){}
ngOnInit() {
this.userForm = this._formBuilder.group({
'firstName': ['',[Validators.required,Validators.minLength(5)]]
});
}
onSubmit() {
console.log(this.firstName);
}
}
HTML:
<form [formGroup]="userForm" (ngSubmit)="onSubmit()" name="userForm">
<div class="form-group">
<label>First Name</label>
<input type="text" [(ngModel)]="firstName" class="form-control" formControlName="firstName">
<p *ngIf="userForm.controls.firstName.invalid && (userForm.controls.firstName.dirty || userForm.controls.firstName.touched)"> Error message </p>
</div>
<button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">Submit </button>
</form>

Show loading image on each api call till response comes in axios library globally

Sometimes, when we call an api,it takes long time to respond so we prefer to show loading image till response come.
there is start and end event in ajax request to show loading image , I want same kind of stuff in axios.
I want to make it globally for each request in axios library for react.
please suggest me something for this.
import React, {Component} from 'react';
import axios from 'axios';
import loading from '../loading.gif' // relative path to image
class Home extends Component {
constructor() {
super()
this.state = {
time: null,
loading: true,
retJobs: []
}
this.getJobs();
}
getJobs() {
axios.get('http://example.com/getData.php')
.then(response => {
this.setState({retJobs: response.data});
console.log(response);
console.log(this.state.retJobs.results.length);
console.log(this.state.retJobs.results);
this.setState({
time: response.data.time,
loading: false
});
})
.catch(function (error) {
console.log(error);
});
}
render() {
let content;
if (this.state.loading) {
content = <img src={loading} alt="loading"/>// or another graceful content
} else {
content = <p className="jobCount">Found {this.state.retJobs.results.length} Jobs</p>;
}
return (
<div>
<h4>Home</h4>
<div className="App-intro">
<div className="container">
<div className="row mainContent">
<div className=" row mainContent">
{content}
{this.state.retJobs.results && this.state.retJobs.results.map(function (item, idx) {
return <div key={idx} className="col-lg-12">
<div className="jobs">
<div className="row jobHeader">
<div className="col-lg-6">
<h1>{item.jobTitle}</h1>
</div>
<div className="col-lg-6">
<h3>{item.locationName}</h3>
</div>
</div>
<div className="row">
<div className="col-lg-2">
<h2>{item.employerName}</h2>
</div>
<div className="col-lg-10">
<p>{item.jobDescription}</p>
</div>
</div>
</div>
</div>
})}
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Home;

Set model date defaultValue to string

I try to build a 'task manager' to log the tasks that my customers send me.
I have my new-task.hbs form
<div id="new-task-form" class="col-md-12">
<form>
<div class="form-group">
<label>Customer</label>
{{input type="text" class="form-control" value=customer placeholder="Add Customer..."}}
</div>
<div class="form-group">
<label>Task</label>
{{textarea class="form-control" value=task placeholder="Add Task..."}}
</div>
<div class="form-group">
<label>Incoming</label>
{{input type="number" class="form-control" value=incoming placeholder="Bring it on..."}}
</div>
<div class="form-group">
<label>Pending</label>
{{input type="number" class="form-control" value=pending placeholder="Don't bring it on..."}}
</div>
<div class="form-group">
<label>Closed Date</label>
{{input type="date" class="form-control" value=closed_date placeholder="Please close me..."}}
</div>
<button {{action 'addTask'}} class="btn btn-primary">Submit</button>
</form>
My controller.
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
addTask: function(){
var customer = this.get('customer');
var task = this.get('task');
var incoming = this.get('incoming');
var pending = this.get('pending');
var closed_date = this.get('closed_date');
//Create new task
var newTask = this.store.createRecord('task',{
customer: customer,
task: task,
incoming: incoming,
pending: pending,
closed_date: closed_date
});
//save to db
newTask.save();
}
}
});
And the model
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
customer: attr('string'),
task: attr('string'),
incoming: attr('number', { defaultValue: 0 }),
pending: attr('number', { defaultValue: 0 }),
closed_date: attr('date'),
created: attr('string', {
defaultValue: function(){
return new Date();
}
})
});
How can i set a model defaultValue for a the closed_date input to a string "Not entered yet"?
If i leave it like this and not enter a value i get an "Invalid Date".
closed_date: attr('date')
If i set this i get the current date.
closed_date: attr('date', { defaultValue: 'Not entered yet' })
From my experience I suggest you leave closed_date as it is (as date) and focus on DISPLAYING Not entered yet in each place that you want to show it when closed_date isn't entered.
For example when you show model values in template you can use:
Closed date: {{if model.closed_date model.closed_date 'Not entered yet'}}
Consider using ember-pikaday for a nice date-selection experience (which also gives you the placeholder functionality you are looking for!).
Furthermore, I'd suggest that you use the model hook of your new-task route to do your model setup. Combine that with ember-data-route to do cleanup on route exit, and you should be good to go:
router.js:
this.route('tasks', function() {
this.route('new');
});
routes/tasks/new.js:
import Ember from 'ember';
import DataRoute from 'ember-data-route';
export default Route.extend(DataRoute, {
model() {
return this.store.createRecord('task');
}
});
Note below how the form field values have been updated to model.fieldName. These values are bound to the model you created in your route's model hook.
templates/tasks/new.hbs:
<div id="new-task-form" class="col-md-12">
<form>
<div class="form-group">
<label>Customer</label>
{{input type="text" class="form-control" value=model.customer placeholder="Add Customer..."}}
</div>
<div class="form-group">
<label>Task</label>
{{textarea class="form-control" value=model.task placeholder="Add Task..."}}
</div>
<div class="form-group">
<label>Incoming</label>
{{input type="number" class="form-control" value=model.incoming placeholder="Bring it on..."}}
</div>
<div class="form-group">
<label>Pending</label>
{{input type="number" class="form-control" value=model.pending placeholder="Don't bring it on..."}}
</div>
<div class="form-group">
<label>
Closed Date:
{{pikaday-input value=model.closedDate placeholder="Please close me..."}}
</label>
</div>
<button {{action 'addTask'}} class="btn btn-primary">Submit</button>
</form>
Note: prefer camelCasedMultipleWordModelAttributeName vs underscored_attribute_name
models/task.js:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
customer: attr('string'),
task: attr('string'),
incoming: attr('number', { defaultValue: 0 }),
pending: attr('number', { defaultValue: 0 }),
closedDate: attr('date', {
defaultValue() { return new Date(); }
}),
created: attr('string', {
defaultValue() { return new Date(); }
})
});
Now the nice part. Here's what your controller action looks like when you do your setup in your route's model hook:
controllers/tasks/new.js
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
addTask: function(){
this.get('model').save();
}
}
});
and for extra credit you could install ember-route-action-helper and move the controller action onto the route and remove the controller completely.