Component Attribute is none - timing issue - dart

I want to do a HTTPRequest based upon a url, which is set in a component attribute. I tried it like shown below, but the dataUrl is always none. It seems as the constructor of the component is executed before the attributes, which are set in the html, are available to the component.
How can I tell the HTTPRequest to wait until the dataUrl variable is available?
component.dart
class TableData {
static List data = [];
TableData() {}
//GETTERS
List get getData => data;
}
#NgComponent(
selector: 'jstable',
templateUrl: 'jstable/jstable_component.html',
cssUrl: 'jstable/jstable_component.css',
publishAs: 'cmp'
)
class JSTableComponent {
#NgAttr('name')
String name;
#NgAttr('data-url')
String dataUrl;
TableData _table_data = new TableData();
final Http _http;
bool dataLoaded = false;
JSTableComponent(this._http) {
_loadData().then((_) {
dataLoaded = true;
}, onError: (_) {
dataLoaded = false;
});
}
//GETTERS
List get data => _table_data.getData;
//HTTP
Future _loadData() {
print("data url is");
print(dataUrl);
return _http.get(dataUrl).then((HttpResponse response) {
TableData.data = response.data['data'];
});
}
}
.html
<jstable name="myjstablename" data-url="table-data.json"></jstable>

Implement NgAttachAware and put your code in the attach method. The attributes are already evaluated when attach is called.

Related

Load generic angular dynamic component

I am trying to make a dynamic component for the popup to create view and edit page for different content. I have made a popup component in which I want to pass a new component name and title of the page. However, I am not getting new component data in the popup component. Please have a look at code, if you need any more detail, please ask. Thanks in Advance.
I have tried to inject service in another component and it gets data on button click, but in the popup component, I am not getting data. For now, I am doing just console.log data to popup.component.ts file but there is no result in console.log.
popup.service.ts
export class PopupService {
isShowing = false;
private popup = new Subject();
loadingPopup = new Subject();
outputEmitter = new Subject();
popupContent: any = {
isShowing: false,
content: null,
contentParams: PopupModel
}
constructor() { }
public getPopup() {
return this.popup;
}
public showLoading(isLoading: boolean = true) {
this.loadingPopup.next(isLoading);
}
public create(component: any, parameters?: PopupModel): any {
this.showLoading(true);
this.popupContent.isShowing = true;
this.popupContent.content = component;
this.popupContent.contentParams = parameters;
this.popup.next(this.popupContent);
console.log(this.popupContent)
}
}
Popupcomponent.ts
export class PopupComponent implements OnInit, OnDestroy {
public popupObservable: Subscription;
constructor(private popupService: PopupService) { }
ngOnInit() {
this.popupObservable = this.popupService.getPopup().subscribe((popupContent: any) => {
console.log(popupContent)
//code here to use createDynamicComponent method }
}
private createDynamicComponent(component: Type<any>): void {
//code here using ComponentFactoryResolver and ViewContainerRef to create dynamic component
}
ngOnDestroy() {
if (this.popupObservable && !this.popupObservable.closed) {
this.popupObservable.unsubscribe();
}
}
}
This is the code where the dynamic component is being called and the popup should be created.
Component.ts
AddRecord(){
this.popupService.create( NewRecordComponent, {
title: 'Add',
emitterMethod: 'saved'
})
}
component.html
<button (click)="AddRecord()">Add</button>
You are not emitting a value from your subject anywhere.
You would need to call popup.next(popupContent).
However, I don't think you have the right model here.
If you're not doing any async calls (api, filesystem etc..) in your getPopup method, then just return the popupContent directly
public getPopup() : {} {
return this.popupContent;
}
You should also define an interface for your popupContent somewhere so you can import it and use a strong interface to avoid runtime errors.
e.g.
export interface IPopupContent {
isShowing: boolean;
content: string;
contentParams: PopupModel;
}
Also, don't expose subjects directly, rather expose an observable linked to the subject.
See When to use asObservable() in rxjs?

How to display variable from json return in text

String empName;
Future<List> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
empName = dataUser[0]['name'];
return null;
}
How to display the variable "empName" in line 2 to line 70 "child: Text('')"
Full code on Pastebin
Try this way.. make pojo class for response data like this way..
class UserData {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
UserData({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory UserData.fromJson(Map<String, dynamic> json) {
return new UserData(
albumId: json['albumId'],
id: json['id'],
title: json['title'],
url: json['url'],
thumbnailUrl: json['thumbnailUrl']);
}
}
make method for api call..
Future<UserData> fetchData() async {
var result = await get('https://jsonplaceholder.typicode.com/photos');
if (result.statusCode == 200) {
return UserData.fromJson(json.decode(result.body));
} else {
// If that response was not OK, throw an error.
throw Exception('Failed to load post');
}
}
after that make global object that fetch data..
Future<UserData> userDataList;
on Button click ..
userDataList = fetchData();
after that you want to print data..
userDataList.then((userData){
print(userData.title);
});
First of all you getUserData() function never returns anything. It seems like you only need the name so this function could look like this:
Future<String> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
return dataUser[0]['name'];
}
Then to set the empName variable you should use setState().
So change your afterFirstLayout() method to this:
#override
void afterFirstLayout(BuildContext context) async {
// Calling the same function "after layout" to resolve the issue.
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
}
Also you seem to want to reload the name once you press the IconButton.
So you might want to override your code with this:
IconButton(icon: Icon(Icons.shopping_cart),
onPressed:() {
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
},
),

How to handle TypeORM entity field unique validation error in NestJS?

I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();

Angular 2 strings remain undefined on first iteration

I'm trying to fetch an object from the server via http.get and it works but the strings remain undefined on the first iteration.
The full object
The integers are ok, the problem is only with the strings.
this.http.get("/Home/getRoom", { headers: headers }).map(res => res.json()).subscribe(q => {
this.timecount = q.timeperquestion; this.player1id = JSON.stringify(q.player1id); this.player2id = q.player2id; }, err => console.error(err),
() => console.log('Complete'));
after the first iteration of the function the strings are shown.
EDIT:
this is the class:
class AppComponent implements AfterViewInit {
public answered = false;
public title = "loading question...";
public options = [];
public correctAnswer = false;
public working = false;
public timecount=15;
public timer;
public roomID;
public player1id;
public player2id;
constructor( #Inject(Http) private http: Http, private Ref: changeDetectorRef) {
}
nextQuestion() {
this.working = true;
this.answered = false;
this.title = "loading question...";
this.options = [];
var headers = new Headers();
headers.append('If-Modified-Since', 'Mon, 27 Mar 1972 00:00:00 GMT');
this.http.get("/Home/getRoom", { headers: headers }).map(res => res.json()).subscribe(q => {
this.timecount = q.timeperquestion;
this.player1id = JSON.stringify(q.player1id);
this.Ref.detectChanges();
},
err => console.error(err),
() => console.log('Complete'));
EDIT 2:
Ok it seems like everything is ok with the strings, when I try to display it with {{player1id}} it shows the right value even on the first iteration.
The "problem" is only with console.log(player1id) that i used to check the string with, it works only after one iteration.
Thank you for trying to help!
You could force the change detection to run using the ChangeDetectorRef class. Inject it and call its detectChanges method:
constructor(private changeDetectorRef:changeDetectorRef) {
}
someMethod() {
this.http.get("/Home/getRoom", { headers: headers }).map(res => res.json()).subscribe(q => {
this.timecount = q.timeperquestion;
this.player1id = JSON.stringify(q.player1id);
this.player2id = q.player2id;
this.changeDetectorRef.detectChanges();
},
err => console.error(err),
() => console.log('Complete'));
}
The reason the properties are undefined is due to the fact that they are actually not defined. Simply set them to an empty string = "" or declare them as a : string like this:
class AppComponent implements AfterViewInit {
public player1id: string;
public player2id = ""; // I usually prefer this as it is assigned a value
// Omitted for brevity...
}
In TypeScript you should either declare the type you're intending to use or assign it such that it is inferred. Otherwise you're kind of missing out on all the glory of TypeScript.

how to write a simple http intercept?

Can somebody explain what is wrong with the following?
void main() {
var intercept = new HttpInterceptor();
intercept.response = (HttpResponse response) => print('we have a response');
Injector injector = ngBootstrap(module: new MyModule());
var interceptors = injector.get(HttpInterceptors);
interceptors.add(intercept);
}
Thanks in advance
EDIT:
And here is how I do an http request (from inside a directive):
#NgDirective(
selector: '.my-selector')
class MyDirective implements NgAttachAware {
#NgOneWay('id')
String id;
Element self;
MyDirective(Element el) {
self = el;
}
void attach() {
final String _urlBase = 'http://www.some-site.com/';
HttpRequest req = new HttpRequest();
req.open('GET', _urlBase + id);
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.onLoadEnd.listen((e) {
if (req.status == 200) {
// do something useful
} else {
print('download failed');
}
});
req.send();
}
}
Requests return successfully but the interceptor never fires. Why?
You must use the AngularDart built-in service "Http" and not the dart:html HttpRequest class.
The HttpInterceptors only work on when using that service which wraps the HttpRequest class.
In other words, inject Http in the directive´s constructor:
MyDirective(Element el, Http http) {
self = el;
this._http = http;
}

Resources