Angular 2 Component property shadowing - html

I have declared one property called foobar in the Angular 2 Foobar component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-foobar',
templateUrl: './foobar.component.html',
styleUrls: ['./foobar.component.css']
})
export class FoobarComponent implements OnInit {
foobar= 'test';
ngOnInit() {
}
}
then in foobar.component.html file I output that property like this:
<div>
<br>
{{foobar}}
</div>
and it outputs word test, however, once I have added element with id foobar:
<div>
<br>
{{foobar}}
<br>
<input type="text" #foobar>
</div>
I'm no longer able to get value from the component and it outputs [object HTMLInputElement], because it was shadowed by the input field id.
How can I get value from the component without changing component property name and input field id?

You can try this
<div>
<br>
{{ this['foobar'] }}
<br>
<input type="text" #foobar>
</div>
but it is not documented and may break in future releases

Try with two-way databinding:
<div>
<br />
{{foobar}}
<br />
<input type="text" [(ngModel)]="foobar">
</div>`
To accomplish that you need the module: #angular/forms in your package.json and donĀ“t forget to import it to your app-module:
import {FormsModule} from "#angular/forms";
#NgModule({
declarations: [ ... ],
imports: [
FormsModule,
...
]
...
})
export class AppModule{ ... }

Related

What is the purpose of <app-control-message> in angular 8 and how it is used?

I need to create a button for uploading a file in my form. I am using tag in my html but it is throwing an error : Can't bind to 'control' since it isn't a known property of 'app-contorl-message'.
Here is my html code -
<div class="col-lg-6">
<div class="form-group">
<div class="custom-file">
<input *ngIf="mode == 'CREATE'" type="file" (change)="onFileSelect($event)"
accept=".jpg,.jpeg,.png"/>
<app-contorl-message [control]="form.get('file')"></app-contorl-message>
</div>
</div>
</div>
here is def of onSelect($event) :
onFileSelect(event) {
this.form.get('file').setValue(event.srcElement.files);
this.fileData = event.srcElement.files;
console.log(event);
}
Thanks in advnace!
In your AppControlMessageComponent you need to create an #Input() named control. To learn more about inputs and output, visit: https://angular.io/guide/inputs-outputs
app-control-message.component.ts
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-app-control-message",
templateUrl: "./app-control-message.component.html",
styleUrls: ["./app-control-message.component.css"]
})
export class AppControlMessageComponent implements OnInit {
#Input() control; // Input whose value parent component will provide
constructor() {}
ngOnInit() {}
}

TypeError: Cannot read property 'bookIsbn' of undefined when used ngModel with anular materials

I went through all the related issues, but can not find any proper answer.
I am getting an error when using the [(ngModel)] with the angular material in forms data binding.
add-book.component.html
<html>
<head>
<title>
Create Book
</title>
</head>
<header>
<h2 class="form_heading"> Create New Book </h2>
</header>
<body class="create_Book_Body">
<form name="createBookForm" #createBookForm="ngForm">
{{libraryItemModel | json}}
<mat-form-field class="form_Field">
<input type="text" pattern="[0-9]*" minlength="5" maxlength="5" min="10000" name="bookIsbn" #bookIsbn="ngModel" [(ngModel)]="libraryItemModel.bookIsbn" matInput
placeholder="DVD ISBN NO" required>
<div *ngIf="((bookIsbn.touched) && !(bookIsbn.valid))">
<div *ngIf="bookIsbn.errors.required">
<mat-error >
This field is required/Invalid
</mat-error>
</div>
<div *ngIf="bookIsbn.errors.minlength">
<mat-error >
ISBN should be of 5 characters
</mat-error>
</div>
<div *ngIf="bookIsbn.errors.pattern">
<mat-error >
Invalid Pattern
</mat-error>
</div>
</div>
</mat-form-field>
</form>
</body>
</html>
add-book.component.ts
import {Component, Input, OnInit} from '#angular/core';
import {FormControl, FormGroup, Validators} from '#angular/forms';
#Component({
selector: 'app-add-book',
templateUrl: './add-book.component.html',
styleUrls: ['./add-book.component.css']
})
export class AddBookComponent implements OnInit {
onSubmit($event) : void {
console.log(($event));
}
constructor() { }
ngOnInit() {
}
}
here I have created a class library item, in which the models are been created and the form data will be bound to.
library-item.ts
export class LibraryItem {
constructor(
public bookIsbn : string
){}
}
app.component.ts
import { Component } from '#angular/core';
import {LibraryItem} from './library-item';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Lib_Manager';
inputText : string = 'initial Value'
libraryItemModel = new LibraryItem('12345');
}
Error
Thanks in advance for considering my issue.
In your html you have used bookIsbn as a form validation input... but what you have done is adding a string as bookIsbn and try to read the properties...
Check angular validation for proper validation..
https://angular.io/guide/form-validation

Angular 5 issue with contenteditable and input in firefox and safari

So I'm creating a table with content-editable cells, I need at the same time to update the visual and the typescript code, unfortunately all alternatives I found didn't work, here is a plunker with identical issue:
https://plnkr.co/edit/PCXNFSUqiHrYedx4E4mW?p=preview
import {Component, NgModule, VERSION} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
#Component({
selector: 'my-app',
template: `
<div contenteditable='true'
(input)='name=$event.target.innerText'
[(innerText)]='name'>
name
</div>
<br>
<div contenteditable='true'
(input)='name=$event.target.innerHtml'
[(innerHtml)]='name'>
name
</div>
<br>
<div contenteditable='true'
(input)='name=$event.target.textContent'
[(textContent)]='name'>
name
</div>
`,
})
export class App {
name:string;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
It works well with google chrome and opera, safari and mozilla wrights the text backwards with textContent and innerText, and the value become undefined with innerHtml.
#Update
For my issue I end up using input-field inside the cells
<td [class.active]="selectedCell.row === rowIndex && selectedCell.colunm === 3"
(click)="setSelectedCell(rowIndex, 3, true)"
class="lassoer-info">
<input [id]="'entity' + rowIndex"
(keydown)="onkeyDown($event)"
[(ngModel)]="rowData.entity"
[disabled]="rowData.inscriptionId || !canEdit"
type="text"
class="input-field">
</td>
I'm just starting with Angular but I think I can spot one mistake. In Javascript it should be innerHTML instead of innerHtml (HTML in upper case).

very basic angular2 form not getting validated

I have a very basic form consisting of a input field and a button. I am trying to use angular2 validators to show an error message when anything other than numbers are entered into the input field and also to disable the submit button when the input field is invalid or empty. For some reason the error message shows regardless of what gets entered... Any idea what i'm doing wrong?
my code:
app.component.html:
<div class="row">
<div class="col-md-4 col-md-push-4">
<form [formGroup]="barcodeForm" role="form" (ngSubmit)="submitBarcode(barcode)" >
<div class="form-group">
<input type="number" class="form-control" id="barcode" placeholder="Enter Barcode" [formControl]="barcodeForm.controls['barcode']" [(ngModel)]="barcode" name="barcode" #focusInput>
<div [hidden]="barcode.valid || barcode.pristine" class="alert alert-danger">A barcode can only consist of numbers (0-9)...</div>
</div>
<button class="btn btn-submit btn-block" [disabled]="!(barcode.valid) || barcode.pristine">Submit</button>
</form>
</div>
</div>
part of app.component.ts:
import { Component, ViewChild, ElementRef, AfterViewInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { RestService } from "./services/rest.service";
import { ProductModel } from "./models/product.model";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
#ViewChild('focusInput') focusInput: ElementRef;
barcode: string;
barcodeForm: FormGroup;
product: ProductModel;
constructor(fb: FormBuilder, private restService: RestService){
this.barcodeForm = fb.group({
'barcode':['', [Validators.required, Validators.pattern("[0-9]+")]]
});
}
In angular2 there are two types of forms: template driven and model driven.
Model driven is defining the form's structure in code and binding inputs to its controls (via formControl and formGroup).
And template driven is using ngModel and defining validators on the template.
I can see most of your code is targeted for model driven which in my opinion is better anyway, but you still have ngModel on your input, do you need it for something?
I dont see you using it anywhere other than barcode.valid which shouldn't work since barcode is merely a string. You want to bind the disabled property to barcodeForms.controls['barcode'].valid instead and then remove the use of ngModel. It might conflict with formControl since both of them initialize a FormControl instance for that element.

Building a wrapper directive (wrap some content / component) in angular2

I'm pretty new building directives with Angular2. What I want is to create a popup directive that will wrap the content with some css classes.
Content
Content can be pure text and headers like:
<div class="data">
<h2>Header</h2>
Content to be placed here.
</div>
Then I want to give this a directive attribute like: popup
<div class="data" popup>
<h2>Header</h2>
Content to be placed here.
</div>
What the directive should do, is to wrap the div inside, lets say:
<div class="some class">
<div class="some other class">
<div class="data">
<h2>Header</h2>
Content to be placed here.
</div>
</div>
</div>
The case i described so far, is this a attribute or structural directives.
import { Directive, ElementRef, HostListener, Input } from '#angular/core';
#Directive({
selector: `[popup]`
})
export class PopupDirective {
}
The other answer is related but different.
For something closer, see this: How to conditionally wrap a div around ng-content - my solution is for Angular 4, but the linked question has some hints about how this might be doable for Angular 2.
I solved this problem with a component and a directive combined. My component looks something like this:
import { Component, Input, TemplateRef } from '#angular/core';
#Component({
selector: 'my-wrapper-container',
template: `
<div class="whatever">
<ng-container *ngTemplateOutlet="template"></ng-container>
</div>
`
})
export class WrapperContainerComponent {
#Input() template: TemplateRef<any>;
}
and my directive like this:
import { Directive, OnInit, Input, TemplateRef, ComponentRef, ComponentFactoryResolver, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[myWrapperDirective]'
})
export class WrapperDirective implements OnInit {
private wrapperContainer: ComponentRef<WrapperContainerComponent>;
constructor(
private templateRef: TemplateRef<any>,
private viewContainerRef: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver
) { }
ngOnInit() {
const containerFactory = this.componentFactoryResolver.resolveComponentFactory(WrapperContainerComponent);
this.wrapperContainer = this.viewContainerRef.createComponent(containerFactory);
this.wrapperContainer.instance.template = this.templateRef;
}
}
To be able to load your component dynamically, you need to list your component as an entryComponent inside your module :
#NgModule({
imports: [CommonModule],
declarations: [WrapperContainerComponent, WrapperDirective],
exports: [WrapperContainerComponent, WrapperDirective],
entryComponents: [WrapperContainerComponent]
})
export class MyModule{}
so the HTML in the end is:
<some_tag *myWrapperDirective />
Which renders as:
<my-wrapper-container>
<div class="whatever">
<some_tag />
</div>
</my-wrapper-container>
You can achieve this with a component attribute selector and Angular 2 Content Projection <ng-content>
#Component({
selector: 'my-app',
template: `
<div class="app">
<div class="data" myWrapper>
<h2>Header</h2>
Content to be placed here.
</div>
</div>
`
})
export class AppComponent {}
#Component({
selector: '[myWrapper]',
template: `
<div class="my-class">
<div class="my-sub-class">
<ng-content></ng-content>
</div>
</div>
`
})
export class MyComponent {
}