How to Attach a Variable to $scope in Angular 2? - javascript

Finding angular 2 syntax is pain - everything is outdated. With that said, what is the proper way to attach variables (simple or objects) to "$scope"? (I realize the idea of controller $scope doesn't exist anymore...or controllers for that matter):
import {Component} from '#angular/core'
#Component({
selector: 'testtag',
templateUrl: './simplecomponent.component.html',
})
export class SimpleComponent {
public myNumber : number = 5;
}
html:
<div>Hi from component template! {{myNumber}}</div>
This code works. I can see "5" in my template output. HOWEVER, everything I have read seems to to say that I need to do something like this.muNumber = 5;. Is that for when plain javascript is used? Is this even relevant when using typescript? If so, how?
tl;dr: What's the proper way to do one way and two-way binding in Angular 2 as of right now? (Now that "final" Angular 2 is out).

Looking at the question, It seems that you want to know how you get 5 as output without using this?
It is printing 5 because you are directly assigning the initial value to property myNumber of class SimpleComponent. Since that is initialized with 5 it prints 5 when rendered.
If you want to fit this in your example this is how your component will look like
export class SimpleComponent {
public myNumber : number;
constructor() {
this.myNumber = 5;
}
}
In short whenever you are trying to access the class property inside constructor or any function inside component you have to use this

Related

Angular 8 viewChild returns undefined

I am trying to access the childView instance but it keeps saying the the childView is undefined.
Here is my code for childViews:
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
#ViewChild(ProjectNavbarComponent,{ read: true, static: false }) private childProjectNavBar:ProjectNavbarComponent;
#ViewChild(QuestionnaireNodeComponent,{ read: true, static: false }) private childQuestionnaireNode:QuestionnaireNodeComponent;
....
onCreateTerminal() {
this.childQuestionnaireDialog.generateQuestionnaireDropDownList();
this.childQuestionnaireDialog.resetFields();
this._hideQuestionnaireDialog = false;
this._modalTitle = 'New Terminal';
this._placeHolderText = 'Terminal Questionnaire Title';
this._terminal = true;
}
...
It says :this.childQuestionnaireDialog is undefined".
It was working with Angular 7.
As per my new knowledge, the #viewChild takes a flag called static. If we put the flag to true, the parent component tries to get a reference to the childView during its own creation. In other words, we could have an instance of the childView in the onInit() method of the parent Component.Basically a one time access because we won't be able to access in any other methods.
The flag set to false, is basically the new way in ivy renderer.
The problem in my case, neither options are working.
I had a similar problem where ViewChild component is undefined in onInit() method.
This fixed the issue:
// Ensure Change Detection runs before accessing the instance
#ContentChild('foo', { static: false }) foo!: ElementRef;
// If you need to access it in ngOnInit hook
#ViewChild(TemplateRef, { static: true }) foo!: TemplateRef;
You must be trying to access the results of a ViewChild query before the view has completed initializing.
So, you can either mark the query static:
#ViewChild('test', {static: true}) test: ElementRef;
... or move the logic to ngAfterViewInit (preferred).
Refer https://angular.io/guide/static-query-migration
In my case i had rendered my child on *ngIf condition. Hence ViewChild was unable to initialize the required element. Have updated the logic to render the component outside *ngIf, there by ViewChild was able to successfully initialize the element.
Hence if possible, when ViewChild is required, alter the logic to render the component outside *ngIf and show appropriate messages. Or Hide the visibility of the elements using CSS, instead of *ngIf.
I spent an hour not finding it on SO, so it may also help someone else:
My mistake was that I used the wrong selector in the angular html.
The component I wanted to reference looks like this:
#Component({
selector: 'app-universal-modal',
templateUrl: './universal-modal.component.html',
styleUrls: ['./universal-modal.component.scss']
})
export class UniversalModalComponent implements OnInit {
Now I used viewchild in its parent:
#ViewChild('universalModalComponent', {static: true}) universalModalComponent: UniversalModalComponent;
And the html selector should be app-universal-modal:
<app-universal-modal #universalModalComponent ></app-universal-modal>
But instead I used new-task selector.
Strange thing there was no compile time error, but a runtime one...
If you are using #viewchildTemplateRef element inside *ngIf condition in .HTML file then #ViewChild selector/reference variable in .ts file will be undefined. So, use [ngClass] instead.
Example: Use [ngClass]={'hide': data.length = 0} instead of *ngIf=data.length>0
According to the docs, the metadata property read is:
`read - read a different token from the queried elements.`
In other words, it's used if you want to read in ViewContainerRef or the Component name instead of the normal ElementRef (which is the default if you leave read out). So putting true as the value is saying to return type true from the element, which as far as I know is impossible.
A much better explanation is here, but the short answer to your problem is take out the read property or to specify ElementRef or the specific type you want.
Slightly different issue on my side, so adding for posterity.
I was initially implementing a #ViewChild and within the scope of the component everything was alright as normal.
However I was implementing a event subscription to triger a method on the child using this.action()withing the event, which lost its reference via the event. (Bad eventing on my side)
Strange one for me, but one solution (not the best fix but before the event manager refactor) was to pass through the reference to this at subscription creation time.
var self = this; // var because we want it to stick around.
subscription.event.subscribe(()=>{
self.callOnThisRef();
})
I'm sure this is by design as it highlighted the problems with my event management. But it was strange to debug.
For Angular 7 we won't get the {static: boolean} parameter instead we are getting {read: any} in my case I had a *ngif directive so the reference of the element was not getting by Viewchild(), what I did is I just checked the element is undefined or not in Component.ts
If you need to access it in ngOnInit hook
Here is an example for removing class by Hot listening
#ViewChild('elref', ) elref: ElementRef;
import { Component, ElementRef, , Renderer2, ViewChild } from'#angular/core';
export class Component implements OnInit {
#ViewChild('elref', ) elref: ElementRef;
constructor(private renderer: Renderer2) {}
ngOnInit() { this.hotlistening(); }
hotlistening() {
this.renderer.listen('window', 'click', (e: Event) => {
if (this.elref !== undefined) { //Here i checked for undefined
this.elref.nativeElement.classList.remove('open');
});
}
}
#ViewChild('test', {static: false}) test: ElementRef;
(I know this question is a bit old. but for those who the guys still find the hint like me)
I had faced this problem also and I found that
{static: true} option might not work properly always when your element is in the parent that has *ngIf.
The workaround to me is that making that element to sub-component
and using it on its own ngOnInit or ngAfterViewInit.
In this way, I don't have to check whether it really exists, subscribe changes, or something complicated extra work.
maybe you could consider it too.
I think this is the simplest solution if it's possible.
I had the error, but what was causing was more hidden. Simply My View Child component had an error on template, where a variable was undefined. I only perceived it because I revised the HTML. It was hard to notice the error, once no error message was triggered.
for child to be available in ngOnInit()
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
for child to be available in ngAfterViewInit()
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
but please make sure your selector of child component <app-questionnaire> </app-questionnaire> in html of parent should not embedded in any condition otherwise it will not follow the above pattern, it will required the condition to be true first then.

Access custom directive with ViewChild / ContentChild

I've been trying to access my custom directive I applied on an element by using #ViewChild(CustomDirective) or #ContentChild(CustomDirective), respectively using the set variable for the first time in my ngAfterViewInit or ngAfterContentInit function.
However, neither of them worked.
At first, I thought that it was due to me loading some content from a webserver, but even after having set static variables, it doesn't work.
Here's what I have:
#Directive({
selector: '[det-insert]',
})
export class DetailedDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
And
... (importing + component stuff)
export class DetailedView implements AfterViewInit {
#ViewChild(DetailedDirective) detView : DetailedDirective;
constructor(...)
ngAfterViewInit(){
alert(detView);
}
}
And the template:
<ng-template det-insert></ng-template>
However, the alert returns undefined.
And I have no clue as to why. Any ideas? I already looked through stackoverflow, but neither is my template obstructed by *ngIf, nor do I start using my queried directive before the proper "AfterXInit" function. I already tried switching ViewChild and AfterViewInit for ViewContent and AfterContentInit respectively, to no avail.
I ran into a similar issue myself. I created a separate module for various alert directives/components. My alert directive was defined and declared in that alert module. I was trying to use the directive in my app module, and in order to get this same example to work, you need to specify that the alert module exports: [AppLevelAlertDirective].
You can still use <ng-template yourDirective></ng-template>, despite it not showing up in the DOM later. This is captured well before the DOM is rendered. Please see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html#!#loading-components
Add DetailedDirective to declarations: [] of your module
<ng-template> isn't added to the DOM, and therefore DetailedDirective can't be found. If you use <div det-insert> it will work

How to use templateRef?

I am trying to find a way to dynamically construct a template in Angular2. I was thinking templateRef might provide a way to do this. But I could be wrong.
I found an example of templateRef being used here.
I was looking at templateRef in this example. I noticed the syntax is [ng-for-template] I also tried [ngForTemplate] cause I know this has changed recently.
So at the moment I have this:
import {Component, TemplateRef} from 'angular2/core';
#Component({
selector : 'body',
template : `
<template [ngForTemplate]="container">
<div class="container"></div>
</template>
`
})
export class App
{
#ContentChild(TemplateRef) container;
constructor() {}
ngAfterContentInit()
{
console.log(this);
}
}
This example throws an error:
Can't bind to 'ngForTemplate' since it isn't a known native property
So firstly I am wondering. What is the right way to do this? The docs don't provide any examples.
Secondly, is there a good way I can add new template logic to my template or dynamically construct a template? The structure of the application can be a very large amount of different structural combinations. So if possible I would like to see if there is a way I can do this without having a huge template with a bunch of different ngIf and ngSwitch statements..
My question is really the first part about templateRef. But any help or suggestions on the second part is appreciated.
Creating your own template directive it's not difficult, you have to understand two main things
TemplateRef contains what's inside your <template> tag
ViewContainerRef as commented by Gunter, holds the template's view and will let you to embed what's inside the template into the view itself.
I will use an example I have when I tried to solve this issue, my approach is not the best for that, but it will work for explaining how it works.
I want to clarify too that you can use any attribute for your templates, even if they're already used by builtin directives (obviously this is not a good idea, but you can do it).
Consider my approach for ngIfIn (my poor approach)
<template [ngIfValue]="'make'" [ngIfIn]="obj">
This will print
</template>
<template [ngIfValue]="'notExistingValue'" [ngIfIn]="obj">
This won't print
</template>
We have here two templates using two inputs each ngIfIn and ngIfValue, so I need my directive to grab the template by these two inputs and get their values too, so it would look like this
#Directive({
selector : '[ngIfIn][ngIfValue]',
inputs : ['ngIfIn', 'ngIfValue']
})
First I need to inject the two classes I mentioned above
constructor(private _vr: ViewContainerRef, private _tr: TemplateRef) {}
I also need to cache the values I'm passing through the inputs
_value: any;
_obj: any;
// Value passed through <template [ngIfValue]="'...'">
set ngIfValue(value: any) {
this._value = value;
}
// Value passed through <template [ngIfIn]="...">
set ngIfIn(obj: any) {
this._obj = obj;
}
In my case I depend on these two values, I could have my logic in ngOnInit but that would run once and wouldn't listen for changes in any of the inputs, so I put the logic in ngOnChanges. Remember that ngOnChanges is called right after the data-bound properties have been checked and before view and content children are checked if at least one of them has changed (copy and paste from the docs).
Now I basically copy & paste NgIf logic (not so complex, but similar)
// ngOnChanges so this gets re-evaluated when one of the inputs change its value
ngOnChanges(changes) {
if(this._value in this._obj) {
// If the condition is true, we embed our template content (TemplateRef) into the view
this._vr.createEmbeddedView(this._tr);
} else {
// If the condition is false we remove the content of the view
this._vr.clear();
}
}
As you see it's not that complicated : Grab a TemplateRef, grab a ViewContainerRef, do some logic and embed the TemplateRef in the view using ViewContainerRef.
Hopefully I made myself clear and I made how to use them clear enough also. Here's a plnkr with the example I explained.
ngForTemplate is only supported with ngFor
<template [ngFor] [ngForOf]="..." [ngForTemplate]="container"
or
<div *ngFor="..." [ngForTemplate]="container"
not on a plain template. It is an #Input() on the NgFor directive
Another way to use TemplateRef
If you have a reference to ViewContainerRef you can use it to "stamp" the template
constructor(private _viewContainer: ViewContainerRef) { }
ngOnInit() {
this.childView = this._viewContainer.createEmbeddedView(this.templ);
this.childView.setLocal('data', this.data);
}

How do you access dom selectors using angular 2

I have a simple script that would add a class to a dom selector.
showParent() {
console.log('show parent');
$("seasonsGrid").toggleClass("grid-layout");
}
How does angular allow you to target these dom nodes whether they are tags or named elements?
in this case $("seasonsGrid") is a reference to
#Component({ selector: 'seasonsGrid', ...
but would be useful to know how to dom traverse using tags and named el's.
what would the ng2 equivalent be for this
var seasonsGrid = $(seasonsGrid);
You could take use of ElementRef/Renderer to get component element
import {Component, View, ElementRef} from 'angular2/angular2';
import {NgClass} from 'angular2/common'; //import when you wanted to use ngClass
import {Renderer} from 'angular2/core';
#Component({ selector: 'seasonsGrid', ...})
export class MyComponent {
let el: any;
let componentElement:any;
constructor(private elementRef: ElementRef, private renderer: Renderer){
//el = elementRef.nativeElement;
componentElement = renderer.selectRootElement(); //will have component root element
}
ngAfterViewInit() {
console.log(el); //nativeElement to process
console.log(componentElement); //nativeElement to process
}
}
But in your case you can think of to have ngClass directive to have it in a place like [ngClass]="{className: expression}"
I must need to do some more homework but from the looks of it angular 2 is by far more convoluted if all that is required just to initialize the states of a toggle class on an element. jquery is 1 line
Angular 2 is a data-driven reactive framework. Instead of thinking about selectors and DOM manipulation code, I encourage you to embrace Angular-think, which is quite different from jQuery-think.
If you want to change a class in a data-driven framework, you first declaratively bind some data to that element's class property in a component template. Then, to change the class, you simply change the bound data. As others have already mentioned, NgClass is the way to do that.
Similarly, component logic should not manipulate the DOM (i.e., it shouldn't call things like toggleClass()), rather it should change data or emit an event (up to its parent).
When Angular change detection runs, it will notice the data changes and update the DOM (or native UI) or propagate the data change to a parent component (for an emitted event) or to a child component (for an input data property change).
Angular-think is all about data-driven changes. And Angular 2 pushes us further down that road than Angular 1 did. (That's a good thing.)
This probably won't answer the question as I'm very new to NG2 (like three days), but here's a few things I've found in the documentation:
<div [class.isStopped] = "isStopped"></div>
<div [style.color] = "isStopped ? 'red' : 'blue'"></div>
<button [attr.aria-label] = "ok"></button>
<div [ngClass] = "{selected:isSelected}"></div>
ngClass is a Directive Property, just like ngStyle and ngModel. For the most part, they just took out the dash.
While we're on Property Bindings:
<img [src] = "vehicle.imageUrl" />
<vehicle-detail [vehicle] = "currentVehicle"></vehicle-detail>
You tie the component (code) to the template so in this case it wouldn't be necessary to get the element in code and change it, just change the Component (view model).

Angular 2 - How to clear an input with a local variable?

Following the guide from the Angular2 site I have this html:
<input #something (keyup)="doneTyping($event)">
<button (click)="add(something .value)">Add</button>
with this controller:
#Component({
selector: 'my-app',
appInjector: [SomeService]
})
#View({
templateUrl: 'index-angular',
directives:[NgFor]
})
class MyAppComponent {
name: string;
stuff: Array<string>;
constructor(someService: SomeService) {
this.name = 'Angular2Sample';
this.stuff= someService.getStuff();
}
add(st: string){
this.stuff.push(st);
}
doneTyping($event) {
if($event.which === 13) {
this.stuff.push($event.target.value);
$event.target.value = null;
}
}
}
When the user hits enter in the input, the doneTyping method clears the input with $event.target.value = null;.
However I can't come with a way of doing the same after pushing the button.
You can pass the input as a parameter in the button
<input #something (keyup)="doneTyping($event)">
<!-- Input as paramter -->
<button (click)="add(something)">Add</button>
And later in the add function
add(st: HTMLInputElement){
this.stuff.push(st.value);
st.value = null;
}
Also, you usually want to avoid interacting with DOM as much as possible. I just checked the Todo app example on the angular2 github and they also access the DOM element, but the last real commit is 2 months old.
If you use data binding you can have a cleaner code which would result in something like :
<input [value]="_newStuff" (keyup.enter)="addStuff()">
<button (click)="addStuff()">Add</button>
Then in your class you can just define the member _newStuff : string, that you can implement addStuff as follow :
addStuff() {
this.stuff.push(_newStuff);
this._newstuff = '';
}
In most cases you might want _newStuff to be a model object that works as an interface like this :
class Stuff {
id : any;
property : any;
otherProperty : any;
}
And then your _newStuff would be _newStuff : Stuff; and you could map it like this : <input [value]="_newStuff.property" (keyup.enter)="addStuff()">.
I know your sample code is very simple and you just try to get it to work, but I believe the data binding way is more in the logic of the framework, and the Form API basically gives tools such as Control, ControlGroup and FormBuilder that help you map your model on your view with Validators and such. It would be too cumbersome on something a bit larger to access the DOM everytime you need to change the view. In the end your example is almost raw javascript executed in an Angular2 context.
Coming back to your example, now imagine you have another event that triggers a new stuff to be added, say a double click or so, you'd need to add another method that handles this event, passing it again the HTMLInputElement, and do basically the same thing as you do on the keyup event handler, thus duplicating code again. With data binding your component owns the state of the view and you can therefore have one simple method that won't be affected by what kind of event triggered it. There you can do the test if the model is valid ( even though for this you'd use the Form API then ).
Anyways, I know this has been answered already, but I thought I would just help to improve the solution given my current understanding of it and how it could be applied to real cases.
You can use a one-direction bindings to access the value of the input. This is a very clear architecture; you don't have to pass DOM elements to the controller.
Template:
<!-- controller.value -> input.value binding-->
<input #myinput [value]=myValue>
<button (click)="done(myinput.value)">Add</button>
Controller:
myValue: string; // If you change this then the bound input.value will also change
// be sure to import ngOnInit from #angular/common
ngOnInit() {
this.myValue = "";
}
done(newValue) {
// ... processing newValue
this.myValue = ""; // Clear the input
}
Here's a good way to actually get your input objects to manipulate
Just need to import ViewChild from #angular/core
Template:
<input #something (keyup)="doneTyping($event)">
Class:
#ViewChild("something") something : HTMLInputElement;
doneTyping(e : KeyboardEvent) {
var text = this.something.value;
if (text != "") {
//DO SOME STUFF!!!!
}
}
Since version 2 of Angular is old now, and we developers are in need much more trend solutions and but we may look old topics in order to find solutions here, I felt I should mention an answer from another topic similar to this.
That answer works and solved my problem in Angular 7 with Type Script. Here its link
There are two ways:
1) I it is created with var or let or const, then it cannot be deleted at all.
Example:
var g_a = 1; //create with var, g_a is a variable
delete g_a; //return false
console.log(g_a); //g_a is still 1
2) If it is created without var, then it can be deleted.. as follows:
declaration:
selectedRow: any;
selectRowOfBankForCurrentCustomer(row: any) {
this.selectedRow = row; // 'row' object is assigned.
}
deleting:
resetData(){
delete this.selectedRow; // true
console.log(this.selectedRow) // this is error because the variable deleted, not its content!!
}
A quick way to do it:
<input #something (keyup)="doneTyping($event)">
<button (click)="add(something.value);something.value=''">Add</button>
A more Angular way to do it:
HTML:
<input [(ngModel)]="intermediateValue">
<button (click)="add()">Add</button>
Controller:
intermediateValue: string;
add() {
this.doStuff(this.intermediateValue);
this.intermediateValue= "";
}

Categories

Resources