Angular2 Recursive Templates in javascript - javascript

I have been trying to follow this tutorial to create a nested tree view. The tutorial is in typescript while I am trying to do a similar thing in javascript with Angular2.
In the typescript code, the tree-view component looks like so:
import {Component, Input} from 'angular2/core';
import {Directory} from './directory';
#Component({
selector: 'tree-view',
templateUrl: './components/tree-view/tree-view.html',
directives: [TreeView]
})
export class TreeView {
#Input() directories: Array<Directory>;
}
In javascript that should convert to:
TreeView = ng.core
.Component({
selector: 'tree-view',
templateUrl: './components/tree-view/tree-view.html',
directives: [TreeView],
inputs: ['directory'],
})
.Class({
constructor: function() {
},
});
However, javascript throws the following error:
EXCEPTION: Unexpected directive value 'undefined' on the View of
component 'function () {'
I believe it's because I'm calling directives: [TreeView] before TreeView has been fully defined. If I remove that directive line, the error goes away. However, I don't know why it would work in typescript and not javascript if typescript simply compiles to javascript. This is the compiled javascript from the typescript code. I'm not sure what I'm missing. Any help would be super appreciated.

This question has been answered a few times
First of all classes are not hoisted. Quoting from MDN
An important difference between function declarations and class declarations is that function declarations are hoisted and class declarations are not. You first need to declare your class and then access it [...]
The documentation for forwardRef says
For instance, forwardRef is used when the token which we need to refer to for the purposes of DI is declared, but not yet defined. It is also used when the token which we use when creating a query is not yet defined.
So it's as easy as adding forwardRef to your code
directives : [ng.core.forwardRef(function() { return TreeView; })]
You can read more about this subject
Forward references in Angular 2
Others questions from StackOverflow

Related

Databinding in Angular, understanding in detail

I am new to Angular and I just started learning it recently. I came across the concept of Databinding in Angular. I was able to understand the syntax and stuff but there were some questions that I couldn't find an answer for. These are the queries I had:
When we export a class from the component TS file, we can use the class properties in HTML file. For eg: Databinding a class property to a HTML element works. But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
Why exactly are we exporting a class for a component to be used? Is the component a class too? If yes, then wehen we use the component are we calling that class and this leads to rendering the HTML and CSS mentioned in the component?
Please let me know.
Answering your question in details requires having an in-depth knowledge about how Angular internally works, but here's a starting point:
I've generated a component using angular CLI:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
public myProperty: number = 0;
constructor() { }
ngOnInit(): void {
}
}
So:
Is the component a class too?
Yes, as you can see from the labels "export class", your component is first of all a regular JS class, which implements the OnInit interface, has an empty constructor and defines a public variable.
If yes, then when we use the component are we calling that class?
Exactly: Angular does a bit of magic (see the next point of the answer), so whenever finds a html tag named <app-example></app-example>, it creates an ExampleComponent instance and replaces that tag with the html you defined on example.component.html
and this leads to rendering the HTML and CSS mentioned in the component?
The magic happens just above the class definition: Angular heavily relies on Typescript Decorators, which are an (still) experimental feature of Typescript. Decorators allows you (or Angular in our case) to alter the behaviour of a class, for example by intercepting methods call, property changes (did you just say databinding?) and constructor parameters (and this is how Angular's dependency injection works).
In the #Component decorators, which is linked to the below ExampleComponent class, you're defining three things:
the selector, or tag name that Angular will search in the DOM and replace with your component's html
Where to find your component's html, which will be linked to each of your ExampleComponent instance
Stylesheets for your component's html
So, when a property on your component changes (for example myProperty), Angular intercepts that change thanks to the #Component decorators you've defined (to understand how, do a little search about decorators), and will re-render his html. Inserting that property value on a paragraph like <p>{{myProperty}}</p> is just a matter of string replacement.
So, now you have the answer to your first question:
But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
It's not the html that knows which component it belongs, it's the component (or Angular that handles that component) that knows which html has to render and which css needs to apply.
Why exactly are we exporting a class for a component to be used?
This is simply to let Angular know that we have defined a component. Exporting something from a .ts file makes it available on the rest of the project, and particularly on the AppModule file, where you will find your ExampleComponent among the declarations array:
#NgModule({
declarations: [
AppComponent,
ExampleComponent
],
// Something else

How to access external javascript file from angular 2 component

I am very new to angular and front end web development, so maybe i am missing something
basic but i did not succeed to search a solution for that issue.
according to that answer: Call pure javascript function from Angular 2 component
and following that example
I am trying to import external .js file to my angular component:
import '../../../src/Plugin/codemirror/mode/custom/custom.js'
#Component({
selector: 'txt-zone',
templateUrl: 'app/txtzone/Txtzone.component.html',
styleUrls: ['app/txtzone/TxtZone.css'],
})
the path is the correct relative path, i know that because if it loads diractly from the url text box via the browser [http://localhost:50371/src/Plugin/codemirror/mode/custom/custom.js]
i can see the file content...
this is the exception that the chrome debugger is throwing:
zone.js:2263 GET
http://localhost:50371/src/Plugin/codemirror/lib/codemirror 404 (Not
Found)
as you can see the path was changed (don`t understand why?)
1. how can i solve this issue?
2. why the path of the .js file is not the referenced path?
3. maybe there is a better way to load external .js file into my component?
it looks quite trivial question but after hours of searching i could not find any answer.
A simple way to include custom javascript functions in your Angular 4 project is to include the item in your assets folder, and then use require to import it into your component typescript.
src/assets/example.js
export function test() {
console.log('test');
}
src/app/app.component.ts
(before #Component(...))
declare var require: any;
const example = require('../assets/example');
(inside export class AppComponent implements OnInit { ...)
ngOnInit() {
example.test();
}

How to import javascript file into angular4 project and use it's function inside component?

I am trying to import openseadragon.min.js file in index.html and using it's function like below,
var viewer = OpenSeadragon({
id: "seadragon-viewer"
});
But it is throwing error like OpenSeadragon is undefined.
Any solutions will be appreciated,
Thank you
Try declaring the variable at the top of your component immediately below your imports, not inside your class export statement.
import { Component } from '#angular/core';
declare var OpenSeadragon: any;
#Component({
...
})
You need angular typings to do this correctly for OpenSeadragon. You also shouldn't be using vars in typescript as it breaks context and causes scope and hoisting issues.
Please see https://github.com/alvaromartmart/types-openseadragon for typings

Angular directive cannot be used?

1) Created a new directive with angularCLI.
import {Directive, ElementRef, OnInit} from '#angular/core';
#Directive({
selector: '[scrollable]'
})
export class ScrollableDirective implements OnInit{
constructor(public el:ElementRef) { }
ngOnInit(){
console.log('its working!')
}
}
2) Angular CLI automatically adds the directive to the app.module declarations
import { ScrollableDirective } from './scrollable/scrollable.directive';
#NgModule({
declarations: [
...
ScrollableDirective
],
3) Try to use the directive as an attribute
<div class="menu-container" *ngIf="menuService.showMenu" [scrollable]>
4) Resulting error
Error: Uncaught (in promise): Error: Template parse errors:
Can't bind to 'scrollable' since it isn't a known property of 'div'.
I have read the official documentation and I seem to be doing all the right things. I cannot understand what I could have missed and why the directive cannot be used.
Try adding the scrollable directive without the [] bindings:
<div class="menu-container" *ngIf="menuService.showMenu" scrollable>
[] would be if you are passing a value to the directive, but you aren't utilizing any #Input values in you directive, so it would not be needed.
The docs use the binding brackets [highlightColor]="'orange'" because it's expecting a string value from the consumer to specify a color. #Input would only be needed if you are needing a value passed to the attribute directive to use in some way.
#Kevin is right that the error is being caused by #Input not being added to the directive configuration, but in this case you don't need it, so avoid the import/export of that decorator.

One Way Binding Angular2 Component With Pure Javascript

I was building an angular2 app in the expected language of typescript and I was having a ton of problem with 3rd-party typings and I was pulling my hair out with dealing with these typescript issue. So I boldly decided to instead give it a try in pure javascript. Crazy huh? Given the extreme lack of help to be found on the web.
But everything is working great with one exception, I can't get one way binding on a control to work. I am using an ES7 transpiler and so some of the annotations even work! So in the end it looks fairly similar to typescript (except for the typing of course).
app component html snippet
<results [results]="results"></results>
ResultsComponent
#Component({
selector: 'results',
templateUrl: '<div *ngFor="let result of results"><result [result]="result"></result></div>',
directives: [ResultComponent]
})
export class ResultsComponent {
#Input() results;
constructor(){};
}
In Typescript this was working fine. The results property in my ResultsComponent would be set to the value set in the app component. In the pure javascript version results is null. The code is reacting to the #Input decorator because if I take it away it complains about it being missing
EXCEPTION: Template parse errors:
Can't bind to 'results' since it isn't a known native property
but somehow the data is not coming through. Any ideas?
I found the answer in the inputs section of the Component decorator thanks to this link. I changed ...
#Component({
selector: 'results',
templateUrl: '<div *ngFor="let result of results"><result [result]="result"></result></div>',
directives: [ResultComponent]
})
export class ResultsComponent {
#Input() results;
constructor(){};
}
... to ...
#Component({
selector: 'results',
templateUrl: '<div *ngFor="let result of results"><result [result]="result"></result></div>',
directives: [ResultComponent],
inputs: ['results']
})
export class ResultsComponent {
constructor(){};
}
... and it works now!

Categories

Resources