traceur
by
Google and babel
created by the JavaScript community. npm install -g typescript
greetText
which takes one argument
called name
. The syntax name: string
represents that the function expects name
to be a string
. Our code will not compile if we call this function with anything other than a
string
. greetText
also has a syntax after the parentheses : string { //code }
. The
colon specifies that we will specify a return type for this function - which in this case is a
string
. You you try returning a number
instead, it will throw an error.
class
, we do this:
constructors
. new
keyword. Use new Person() to create
a new instance of the Person class, for example.
Properties
:Methods
:any
type). Note that a void
value is also a valid any
value. Constructors
:constructor
. They can optionally take parameters but
they can't return any values, since they are called when the class is being instantiated (i.e. an
instance of the class is being created, no other value can be returned). Inheritance
:extends
keyword. =>
functions are a shorthand notation for writing functions. npm install -g @angular/cli
To see the full list of features, you can run ng help
to output the current help
documentation.
export
keyword. Modules export values that they
want to expose to the application and can keep other parts of the internal logic private. Then, in order
to use an exported value, you have to first import it from another module. Executing an Angular
application is fundamentally loading a module that contains the application bootstrapping logic, which
in turn starts to load and trigger additional modules.
ng new stocks
cd stocks
ng serve
http://localhost:4200
to see the landing page. Asset | Role |
---|---|
e2e | End-to-end testing folder, contains a basic stub test |
node_modules | Standard NPM modules directory, no code should be placed here |
src | Source directory for the application. All your application code goes here. |
.editorconfig | Editor configuration defaults |
.angular-cli.json | Configuration file for the angular-cli . angular-cli.json file has been
renamed/replaced with angular.json in version 6 of Angular. SO Link
|
karma.conf.js | Karma configuration file for unit test runner. Unit tests are run using karma. |
package.json | Standard NPM package manifest file. Difference between "^1.4.0"
and "~2.0.1" version numbers.
|
package-lock.json | It stores an exact, versioned dependency tree rather than using starred versioning like package.json itself (e.g. 1.0.*). This means you can guarantee the dependencies for other developers or prod releases, etc. |
protractor.conf.js | Protractor configuration file for e2e test runner. This is now under the e2e folder. End-to-end testing is done using protractor. |
README.md | Standard readme file, contains starter information |
tsconfig.app.json | TypeScript compiler configuration for apps |
tsconfig.json | Default configuration file for TypeScript compiler |
tsconfig.spec.json | TypeScript compiler configuration for unit tests |
tslint.json | TSLint configuration file for TypeScript linting rules |
Asset | Role |
---|---|
app | Contains the primary App component and module |
assets | Empty directory to store static assets like images |
environments | Environment configurations to allow you to build for different targets, like dev or production |
favicon.ico | Image displayed as browser favorite icon |
index.html | Root HTML file for the application |
main.ts | Entry point for the web application code |
polyfills.ts | Imports some common polyfills required to run Angular properly on some browsers. What are polyfills |
styles.css | Global stylesheet |
test.ts | Unit test entry point, not part of application |
src/app/app.component.ts
in the project. @Component
annotation declares that this class is a component by accepting an
object. selector
property that declares the HTML selector of the component.
That means the component is used in the template by adding an HTML tag
<app-root></app-root>
. Know that with this selector we're defining a new
HTML tag
that we can use in our markup. The selector
here indicates which DOM
element this component is going to use. In this case, any <app-root></app-root>
tags that appear within a template will be compiled using the AppComponent
class and get any
attached functionality. templateUrl
property declares a link to a template containing an HTML template. styleUrls
property contains an array of links to any CSS files that
should be loaded for this component. title
. The
value is what you should see rendered in the browser, so this is the source of the value
that ultimately appears.
src/app/app.component.html
in the project. {{title}}
with the value of the title property from the component.
This is called interpolation and is frequently used to display data in a template.
src/app/app.module.ts
AppModule
, and NgModule
is the decorator. NgModule
decorator takes an object with a few different properties. declarations
property is to provide a list of any components and directives to make
available
to the entire application. imports
property is an array of other modules upon which this module depends - in this
case, the BrowserModule
(a collection of required capabilities). If you ever include other modules, such as third-party modules or
ones you've created, they also need to be listed here. providers
property, is empty by default. Any services that are created are to be listed
here.
providers
is used for dependency injection. So to make a service available to be
injected throughout our application, we will add it here.bootstrap
property defines which components to bootstrap at runtime. Typically, this will
be the same App component, and the CLI already set it up for us. AppModule
: The body of the class is indeed empty. But that decorator
above the class (@NgModule
) is giving that class its functionality. So really, that class isn't
empty. It just doesn't require any extra logic after the decorator is applied to it.
bootstrapModule
takes a class as input and assumes that that class is decorated with @NgModule
import
to import a class at the top of the file vs using
imports
to add a module: You put something in your NgModule
's imports if you're
going to be using it in your templates or with dependency injection.
angular.json
file. One of the properties is the main
property. By
default, it points to the src/app/main.ts
file. main.ts
file as the first set of instructions. src/app/main.ts
platformBrowserDynamic
object is used to tell Angular which module is being loading, and
in this case that's the AppModule
. It is important to understand that this is the point where
the code begins to execute. platformBrowserDynamic
. Your Angular application can start off in many ways,
but when you run on the browser you have a specific way of bootstrapping the application and that is defined
in @angular/platform-browser-dynamic
. In short these packages contain angular features which
make getting an Angular app up and running possible in the browser. Bootstrapping is essential and one of
those features.src/index.html
file:
app-root
element and replace it with the rendered component. main.ts
. main.ts
bootstraps the AppModule
. The control passes to the app.module.ts
where it sees
the field bootstrap
and figures out which component to use as the first in order to bootstrap
the application. Control then goes to the app.component.ts
class where it sees the templateUrl
that it has to serve along with the selector
app-root
.
$rootScope
to also communicate between controllers, but it should be avoided as much as possible since it is a global
scope. ng generate service services/stocks
.
The CLI will generate the files in the src/app/services
directory. StockInterface
interface is defined and exported for other components to use. This
provides a TypeScript definition of what a stock object should contain, which is used by TypeScript to
ensure the use of the data remains consistent. StocksService
class is exported and is decorated by the Injectable
decorator. The @Injectable()
decorator marks it as a service that can be injected. The
decorator
is used to set up the proper wiring for Angular to know how to use it elsewhere, so
if you forget to include the decorator, the class might not be injectable into your application. HttpClient
service is injected using the TypeScript
technique of declaring a private variable called http
and then giving it a type of HttpClient
.
load()
method makes a call to the HttpClient
service to load the data for
current stock price values. The HttpClient
service is called and returns an observable, which
is a construct for handling asynchronous events, such as data from an API call. this.http.get<Array<StockInterface>>
is the type variable. This is a
feature of TypeScript that tells it what kind of object it should expect. In this case it will expect
to get an array of objects that conform to the StockInterface
. src/app/app.module.ts
file
stocks.service.ts
file
above: constructor(private http: HttpClient){ }
. Because we declare the type as
HttpClient
(which is a known service in Angular), the application will use the injector
to ensure that the http
property contains an instance of the HttpClient
service.
HttpClient
), it calls a factory function from the provider and returns the requested object.
NgModule
's providers
array is
available
to be injected in your application code (hence we add StocksService
above in app.module_v2.ts
to the providers
array).
@Injectable
decorator ng generate component components/summary
src/app/components/summary
directory. [ngClass]="{increase:isPositive(), decrease: isNegative()}
is a special kind of
attribute known as a Directive. Directives allow you to modify the behavior and display of DOM
elements in a template. NgClass
directive, for example, allows you to change the list of
classes that are attached.NgClass
directive is able to add or remove CSS classes to and from the element. It's
assigned a value, which is an object that contains properties that are the CSS class names (if you go into
summary.component.css you will see that there are CSS classes with names "increase" and "decrease"), and
those properties map to a method on the controller (to be written). If the method returns true, it will add
the class; if false, it
will be removed. In this snippet, the card will get the increase
CSS class when it's positive,
or the decrease
CSS class when it's negative.
{{stock?.symbol?.toUpperCase()}}
. The double curly braces syntax is the way to
display some value in the page (interpolation). The content between the braces is called an Angular
expression and is evaluated against the controller (like the directive), meaning that it will try to
find a property on the controller to display. If it fails, normally it will throw an error, but the safe
navigation operator ?.
will silently fail and not display anything if the property is missing.
{{stock?.lastTradePriceOnly | currency:'USD':'symbol':'.2'}}
shows another feature of Angular
called pipes, which are added directly into the expression to format the output. The interpolation
expression is extended with a pipe symbol, |
, and then a pipe is named and optionally
configured with values separated with the colon :
symbol. Pipes only modify the data before
it is displayed, and do not change the value in the controller. The double curly braces indicate that you
want to bind the data stored in the stock.lastTradePriceOnly
property to display it. The data
is piped through the Currency pipe, which converts the value into a financial figure based on a USD figure,
and rounds to two decimal points.
{{stock?.change | currency:'USD':'symbol':'.2'}} ({{stock?.changeInPercent | percent:'.2'}})
has two different interpolation bindings with a currency or a Percentage pipe. The first will convert to the
same currency format, but the second will take a percentage as a decimal, such as 0.06, and turn it into 6%.
Note that the round brackets in the second interpolation are not the part of any syntax, they are just there
because the UI needs that value to be in round brackets.
summary.component.html
template needs a controller to wire up the data and the methods.
Make the following changes to the src/app/components/summary/summary.component.ts
file. ngOnInit
if we already have a constructor
?
stock
, which is preceded
with the Input
annotation. This indicates that this property is to be provided to the
component by a parent component passing it to the summary. Properties are bound to
an element using an attribute, as you can see here - this (hypothetical) example will set the value of
stockData of the parent component in the stock property of the Summary component:<summary [stock]="stockData"></summary>
stock
@Input
property for the Summary component to consume. @Input
decorator.
src/app/components/summary/summary.component.css
file. :host
selector is used because we want components to be as self-contained as possible.
This relies on the Shadow DOM concepts discussed earlier. When Angular renders this component,
it will modify the output to ensure that the CSS selector is unique and doesn't accidentally interfere with
other elements on the page. src/app/app.module.ts
file and you'll see that the CLI already modified
the module to include the Summary component in the App module. src/app/app.component.ts
file. src/app/app.component.html
file to use the Summary Component. *ngIf="stocks"
, which is a directive that will only render the contents
inside the element when the expression is true. In this case, it won't render the Summary component until
the stock data has been loaded. ng generate component components/dashboard
. This will output new
files into the src/app/components/dashboard directory for the HTML, CSS, controller, and unit test. It also
adds the component to the App module (app.module.ts) to be immediately consumable. src/app/components/dashboard/dashboard.component.ts
DashboardComponent
class is the component controller, and it declares that it
must implement the requirements of OnInit
. If it doesn't, TypeScript will fail to compile the
code and throw an error. It then has two properties: an array of stocks and an
array of strings that represent the stock symbols to display. Initially they're empty arrays,
so we'll need to get them loaded for the component to render. constructor
method runs as soon as the component is created. It will import
the Stocks service onto the service
property and then request the current list of stock
symbols from it. This works because this is a synchronous action that loads a value directly from memory.
ngOnInit
lifecycle hook to call the service to load the stock
data. It uses the list of stock symbols that was loaded in the constructor. We then subscribe
to wait for the results to return and store them in the stocks property. This uses the observable approach
to handling asynchronous requests. We are using observables because the HttpClient
returns an
observable for us to receive the response. This is exposed as a stream of data, even though it is
a single event.
src/app/components/dashboard/dashboard.component.html
file.
*
NgFor
will then create an instance of the Summary component for each of the stock
items. It binds the stock data into the component. Each copy of the Summary component is distinct from the
others, and they don't directly share data.
selector
of the child component[nameOfDataFieldUsedInChildComponent]="nameOfDataFieldUsedInParentComponent"
. Remember to
annotate the nameOfDataFieldUsedInChildComponent in the child component with @Input
controls
, which are the
various types of inputs and fields the form may contain (such as a text input, a checkbox, or some custom
lement). ng generate component components/manage
. Now update the
src/app/app.component.html
so that it is the app-manage
component that is beign
served. FormsModule
to our application, because we are going to use
the form features that aren't automatically included by Angular. Open up the
src/app/app.module.ts
file and add a new import:
import { FormsModule } from '@angular/forms';
src/app/components/manage/manage.component.ts
and add the following: OnInit
lifecycle hook, because it's a synchronous
request to get data that exists in memory.
(submit)="add()"
attribute is a way to add an event listener, known
as an event binding
. When the form is submitted (which is done by pressing Enter), it
will call the add
method. Any attribute that's surrounded by parentheses is an event
binding, and the name of the event should match the event without the on
(onsubmit
is submit
). [(ngModel)]="stock"
attribute is a two-way binding that will sync the value of the
input and the value of the property in the controller anytime it changes from either
location. This way, as the user types into the text field, the value will be immediately
available for the controller to consume. When the user hits Enter, the submit
event
fires and will use the value of the stock property when adding the new symbol. symbol
, it will create
a local variable called symbol
, create a new table row that binds the value, and a button
that's for removing the item. remove
button contains another event binding, this one to handle the click
event. The (click)="remove(symbol)"
attribute adds an event listener to the click
event and will call the remove
method in the controller, passing along the symbol.
Because there are multiple instances of the button, each one passes along the local variable to know which
symbol to remove.
src/app/app.routes.ts
RouterModule
is used to activate the router and accepts the routes configuration when
it's initialized. We also import the two routable components, the Dashboard and Manage components, so we
can reference them properly in our routes configuration. AppRoutes
, which is assigned to the result of
RouterModule.forRoot(routes)
. It's a way to pass configuration to the module. In this case,
we're passing the array of routes. We export this so we can import it into our App module and register it.
src/app/app.module.ts
:
src/app/app.component.html
file, make the following changes:
@Input
Decorator @Output
- Custom Events and Event Emitters npm install -g npm
. The -g
flag means that it will be installed globally (gives the ability to run commands on the CLI from
anywhere). Check the version installed by running npm -v
or node -v
npm install -g @angular/cli
ng
will show the available commands that you can run. Had to run this in admin version of Command Prompt. The idea here is that Angular is not just a web
framework, but rather an entire ecosystem of tools that help you to build web frameworks. ng
commands: "ng.ps1 cannot be loaded because running scripts is disabled on this system", run the following
command in the Command Prompt while logged in as the Admin: powershell Set-ExecutionPolicy RemoteSigned
. Source. ng new nameOfTheProject
in order to create a new directory and a new Angular app. The directory created will have name as nameOfTheProject.
(Angular Routing: No, Stylesheet: CSS) npm start
. This will deploy the application on the port 4200 and can be accessed here: http://localhost:4200/ app-root
tag below is not a standard browser element, we have
created it. We have constructed it using a construct that is known as a Component. A Component allows us to link some style (styleUrls
) and html
(templateUrl
), with some typescript logic (interpolation operator: {{ }}
). Anything within {{ }}
is evaluated as a JS expression within
the context of the component. @Component
is a decorator that identifies the class as an Angular component. app-root
). This allows us to extend the browser functionality with our own
functionality. @Component
associated with the html, and the html can access values in the
component using {{ }}
.
app.component.css
file defines css rules that are going to be visible only inside the app.component.html
page. <input [value]="data.courseTitle">
is useful when you want to pass in some data to the template that is available at
the component class. <img (click)="onRocketImageClick()" src="./rocketImage">
. Here, (click)
specifies the type of event that we are listening for on the
img
element. The function onRocketImageClick()
will have to be added to the component of this html. #
. Like so: <input [value]="expression" #titleInputBox>
. This is called a "Template Reference". We can now use this reference titleInputBox to refer to the
input
element in other parts of the template. The reference can also be injected into the component associated with the html as well. input
box and updating it to reflect on the page using <span>{{ data.courseTitle }} app is running!</span>
. What if an attacker tries to
input some text that is wrapped inside a <script></script>
tag? Angular sees that you are using the value input into the input textbox as a plain
string in your template, and hence will automatically escape any html/script tag that it finds. C:\MY_BACKUP\LearningStuff\Angular8\DeepDiveTutorials\from-github_angular-course
npm install
and npm start
.
ng generate component component-name
is the command that we can use for generating our own custom component using the CLI. This will generate the
following four files: @Component
decorator means that we can use the selector used in that component to create custom html elements of our own.
@Input
Decorator[]=""
and @Input()
syntax. @Input
decorator. The idea here is to define
certain variables, annotated with @Input()
on the course-card component, which will be read by the template associated with the component. The parent
component (in this case, the app.component), will pass the required data to the course-card component through the input property syntax that we saw in Chapter 4
above.
@Input()
to the course-card component. Alternatively, we can pass in the entire Course object.
@Output
- Custom Events and Event Emittersbutton
input on the course-card template. Then we add a click handler to the button by using the (click)=""
syntax. EventEmitter
class from Angular Core. Refer
the below setup to see how to do this. Note that there is a difference in the way that Angular treats native browser events and custom events. Native events, like the click
event that we saw above, are automatically bubbled to the parent components. But that is not the case with custom events. Custom events do not automatically bubble to their
parent components. (courseSelected)="handleCourseSelectedEvent($event)"
to the first app-course-card, handleCourseSelectedEvent would be called as well. ngFor
Core Directive (also conditionally add styles to HTML elements)*ngFor
is a structural directive. A structural directive allows us to change the structure of a page
based on a javascript expression. *ngFor
also gives you some auxiliary features such as index
, first
, last
, even
, odd
. index as i
allows us to access the index of the item currently iterating and assign it the name i. first
tells us if the element currently in
iteration is the first element of the collection or not. Same for last
as well. even
and odd
tell us whether the elements are the even or
odd numbered iterations respectively. ngIf
Directive and the Elvis Operator ?
ngIf
is a directive used for showing/hiding certain parts of the page from the user. ngIf
is *ngIf="expression"
. If the expression evaluates to true
, then the element on which
the ngIf is being applied will still be present on the screen. If the expression evaluates to false
, the element gets hidden. If you "Inspect" the webpage
at this point, you will see that the element has been completely removed from the DOM. It is not just hidden with CSS. false
, else it would be evaluated to true
. You could also add a function call here that would be resolved at
the level of the component.
?
that allows us to check whether the object is null or not. Consider the below example where the first item
in the courses array is undefined
. This would create a NPE when we try to access course.description or course.iconUrl in the
course-card.component. We can prevent this by doing course?.iconUrl and course?.description. Instead of having to use suffix every usage of course
with ?
, we can instead conditionally show the card depending on whether the course is null or not.
*ngIf
is the else
clause. We saw above how to hide the image when the iconUrl was null. Let's say that
instead of just hiding the element, we wanted to present some text to the user like "No Image Available". We do this by adding an else
clause. The
else
block requires a reference to a template block that is going to contain the message. The template block is created using ng-template
and we
pass it to the else
clause by using the template reference #
syntax that we have seen before. This is an example of how we do that:
ngClass
Core DirectivengClass
directive is used to conditionally add CSS classes to the template. class
list by using ngClass
directive. ngClass="expression"
directive allows us to pass an argument that turns on or off certain CSS classes. The expression can take multiple
kinds of arguments. We can pass it a string, an array, or a configuration object.
ngStyle
Core DirectivengStyle
directive. ngClass
or ngStyle
to pass in constant styles to the template. These should
be used only in cases where the styles themselves are dependent on the data that is coming into the component. For instance, a good usage would be if we wanted to set the
background image of the cards to the course image that was being returned from the server. In that case we could use the ngStyle
to set the background image
using the background-image: url(course.iconUrl)
.
ngSwitch
Core DirectivengIf
directive. So it offers us just two options. Sometimes, we want more. ngSwitch="expression"
directive is the expression that we want to switch on, which in this case is going to be
course.category. We use ngSwitchDefault
to handle the default case.
ng-container
Core Directiveng-container
docs. ngIf
, ngSwitch
), we had clear parent elements onto which we could apply these directives.
This is not always the case. We might find ourselves in a situation where we do not have a single parent component onto which we can apply the directive (?). div
just for being able to apply the directive, use ng-container
instead, because it will not create extra DOM elements. Refer Chapter 24 for an example.
slice
pipe for example allows us to slice arrays similar to how we would do it with the JS
slice
method. In the below example, we are slicing courses array such that we will take the elements from index 0 (inclusive) to 3 (exclusive). Hence,
irrespective of the number of elements in the courses array, our list will contain only 3 elements. Also note that here we are passing in multiple args to the pipe by
separating them with the :
operator.
json
pipe which will print the entire json object to the page. Another helpful pipe is the keyvalue
pipe that
prints an object as key, value pairs.
@ViewChild
Decorator - How Does it Work?@ViewChild
docs. ng-template
, giving it a template reference, and then accessing that element through that
reference. But sometimes our component (.ts file) needs a reference to the HTML elements defined in the template. For this, we make use of the @ViewChild
and
@ViewChildren
decorators. @ViewChild()
decorator, we need to specify how we want to fetch the element. There are multiple ways to do this that you can see in
the doc link above. In the below example we are using the component type. @ViewChild(CourseCardComponent)
query. Had there been multiple such
elements, then the @ViewChild
selector would have returned only the very first element that it found.
@ViewChild
- Learn the multiple View Query Configuration Options@ViewChild
, we can also query based on a template reference. This is useful in the case when we have multiple components of the same type.
@ViewChild
to inject components. But we can also use this to inject plain HTML elements. ElementRef
type from Angular Core allows us to handle plain native HTML elements. Notice in the below example that when we logged the component, the type
printed in the console was the component type, i.e. CourseCardComponent. But when we logged the native HTML element, we get the ElementRef
type logged
instead. The ElementRef
object contains a property called nativeElement
which corresponds to the div.courses element. If you hover over it, the
div on the html page will get highlighted. Hence, the @ViewChild
decorator will give you different behavior depending on whether you are using to query plain DOM
elements, or if you are querying a component.
@ViewChild()
like @ViewChild('cardOneRef', {read: ElementRef})
. As we can see in the below example, we are now
getting the app-course-card for the second card printed on the console.
@ViewChild
?@ViewChild
annotation populated? @ViewChild
decorator are already filled in? AfterViewInit
. This will force us to implement the ngAfterViewInit()
method. And it is in
this method where we can earliest access the references populated by the @ViewChild
decorator. It is the Angular framework itself that calls this method after the
references are filled in. Hence, ngAfterViewInit
is the place to put some initialization logic that needs access to the @ViewChild
elements.
ngAfterViewInit
. This will throw a ExpressionChangedAfterItHasBeenCheckedError
. Read about the error
here on the Angular Site. Also has a video lesson about why this error is caused, and how to fix it. ExpressionChangedAfterItHasBeenCheckedError
when an expression value has been changed after change
detection has completed. Angular only throws this error in development mode. The goal of Change Detection is to keep your model (ts code) in sync with the template (your HTML).
It does this by looking for data changes in the component tree from top to bottom. The below image is a simplified breakdown of the steps that Angular performs when the
component is first initialized. Note that the ngAfterViewInit
runs after Change Detection. Any code that runs here should not attempt to update the view.
@ViewChild
annotations, we can query anything at the level of the component
itself. You cannot query elements that are either in the parent component, or elements that are in the children component either. What this means is that, note in the above
images that the app.component.html makes use of the app-course-card. The course-card.component makes use of the img tag. If you added a template
reference to that img, and tried to fetch that image in the app.component.ts, you would get undefined. @ViewChild
queries are restricted to the template that is associated with the component in which the decorator is being used.
@ViewChildren
Decorator and QueryList
In Detail@ViewChildren
is similar to @ViewChild
, but the difference being that instead of querying for just one single element, the
@ViewChildren
allows us to query for a collection of elements. *ngFor
loop. Suppose that we want to get a
reference of all the app-course-card that are created. We can do that by using the @ViewChildren
decorator. QueryList<CourseCardComponent>
, and not something like a list of CourseCardComponent
as one might expect. Also, just like with @ViewChild
, we can specify what type of elements we want to extract. So instead of obtaining references to each of the
CourseCardComponent, we would like to get a reference to the DOM element of each component. Observable
to subscribe to the changes in cards. The Observable
will emit multiple values as the collection changes
over time.
ng-content
In Detail@Input
, we can pass in entire custom HTML as input to child elements as well. ng-content
tag. The content inside the course-card tag is being projected into the template using ng-content
. Any
content that we have between the opening and the closing course-card tag is going to get projected into the template using ng-content
. ng-content
is going to show any content that is between the opening and closing tags of the component.
ng-content
tag. In that case we can use the select
property of the ng-content
tag. There are different ways in which we can restrict the content that will be projected in the ng-content
tag. We can
restrict using the element tag or using the class on the element. Below image shows how.
ng-content
tag in order to catch any elements that would be left over after using the select
property. In the below example,
the "Edit Description:" does not have a CSS selector that is used in either of the ng-content
's select
property. In this case, it is the third
ng-content
that enables us to project all the remaining stuff.
@ContentChild
Decorator@ViewChild
and @ViewChildren
decorators to query the template of a given component so that the
component class can have a direct reference to some elements in its own template. The scope of this query was restricted to the template of the component and nothing else.
ng-content
. One idea could be to add template reference to the element that we
are interested in, in the app.component.html as below. But as you can see, it prints undefined and does not work. This is expected behavior, since although the
@ViewChild
can see within the template, it cannot see within the projected ng-content
@ContentChild
. This works similar to how @ViewChild
works,
except that it can see only within ng-content
. You cannot query other elements that are defined in the template with it. The below image shows the scope of
the @ContentChild
. As you can see, you can query elements from the parent element using @ContentChild
, but only those elements that are defined
within the opening and closing braces of the course-card component.
@ViewChild
query, the @ContentChild
decorator can also be used to query components inside the content of the component.
In the above examples, all the content within <course-card>...<course-card/>
were native DOM elements. But we can also have components within the
course-card
component, as we are having in the below example. We have taken the img and extracted it to a component of its own. So what we have managed to
do is to project a component inside another component. Hence, we also ended up changing the way we were querying the element using the @ContentChild
decorator as
well. In the below example, the decorator is going to return an instance of CourseImageComponent. But optionally, we can pass in
@ContentChild(CourseImageComponent, {read: ElementRef})
in order to get a reference to the underlying element itself (just like we did with @ViewChild
).
@ContentChildren
and the AfterContentInit
Lifecycle Hook@ViewChildren
, @ContentChildren
allows us to query multiple elements. ngAfterViewInit()
to get the content. But this is not the earliest point at which Angular has populated these references for us.
We can have the class implements
AfterContentInit
which will force us to override the ngAfterContentInit()
method. This method is the
earliest point at which the references have already been populated for us. You can see this in the console log prints below. The ngAfterContentInit()
is called
before the ngAfterViewInit()
and already has all valid references. @ViewChildren
is QueryList<CourseImageComponent>
. ng-content
tag in order to see the elements of the parent component. In the below
example, we have removed the ng-content
from the course-card template, and we are still able to query the elements. Ofcourse, doing this also means that the
images will no longer show in the page either. But you can still programmatically access them is what I am saying.
ng-template
ng-template
on how to show alternate element in case an image is not provided. ng-template
is that the HTML associated with the template is included in the DOM only if the template reference that is being used to
refer to the ng-template
is actually being used somewhere in the page. Else, the associated HTML is not included (like how elements that use the *ngIf
directive are not included in the DOM if the expression evaluates to false). Refer lecture if this is not clear. ng-template
.
ngTemplateOutlet
ng-template
that are defined within the template. In addition to that, we can also give to the ng-template its own private
variable context that is going to be visible only inside the ng-template
itself. ng-template
in your HTML does not instantiate it? In the below example, the #blankImage
ref is not being used anywhere. Hence, we need to manually instantiate it in order for it to show up in the DOM. We do this using the ngTemplateOutlet
structural
directive. let-courseName
and let-courseNumber
below are how we declare variables. These variables will be visible only within the ng-template
.
ng-template
makes use of two variables: courseName and courseNumber. Hence, when we instantiate the template using
ngTemplateOutlet
, we will have to pass in concrete values for these variables. This is done by using a context{}
object. let-courseName="description"
is how we assign a value to the courseName variable. The point to note here is that the expression will be evaluated
using the context
value that has been passed in. So only the variables defined within the context
object will be in scope here. ng-template
twice, each time with custom context
data to show that it is truly parameterized. div
element just so we could use *ngTemplateOutlet
directive on it. Instead of using the div
, we
could just use the ng-container
and avoid introducing another div as per Chapter14.
@Input
of type TemplateRef<any>
in the
course-card component. The template itself is defined in the parent component. It is then passed as an @Input
to the child component, where it is
instantiated using the ngTemplateOutlet
and conditionally shown.
*ngIf
, *ngFor
, *ngSwitch
, etc. Structural
directives are attributes that are added to an element. Structural directives are directives which change the DOM layout by adding and removing DOM elements. Structural
directives normally begin with a *
. required
, disabled
, changes the appearance of the element.
ng generate directive folderName/directiveName
. A sample example is
ng generate directive directives/appHighlighted
. This will create a directive called appHighlighted in the directives folder.
@HostBinding
in Detail - DOM Properties vs Attributes$0
. Now you can refer to this element using $0
. Go to console, and type in $0.className
. This will print out the css classes that are
currently assigned to this element. The list would be empty right now. Now when you do $0.className='highlighted'
, it will assign the highlighted css class
that you have defined in your styles file to this element. It doesn't work with the highlighted class for some reason. warn is another class that is defined in
the same css file, and that works.
@HostBinding
Decorator. The @HostBinding
decorator allows us to modify DOM properties of the element onto which it is applied. The @HostBinding
can be used to bind to any DOM property of the host
element.
class.className
that we can
use to do it.
Similarly, we can also use a shorthand for adding styles as style.styleName
[appHighlighted]
. The square brackets around its name specifies that it's an input property, but so
far we have not been passing in any expression into it. Let's change that.
[disabled]="true"
disables the button, and doing
[disabled]="false"
enables it.
[attr.disabled]="false"
, the button still stays disabled, because the value of the attribute does not matter.
@HostBinding
to set DOM properties. But we can also use it to set HTML attributes. We are changing the attr.disabled
attribute below.
@HostListener
- Handling Events in Directives@HostListener
decorator. @HostListener('args')
takes as args the DOM event that we want to detect. We can also pass-in as args the native event that emitted by doing
@HostListener('mouseover', ['$event'])
exportAs
syntax - When to Use it and WhyexportAs
syntax to export the directive. *ngIf
are plain angular attribute directives that have the special ability of instantiating the ng-template
onto which they are applied.
ng-template
in the component by using TemplateRef
,
ViewContainerRef
, and then calling createEmbeddedView
on the instance of ViewContainerRef
and passing in the instance of
TemplateRef
.
styleUrls: ['./course-image.component.css']
. The styles that are specified in this class will only be visible for the template associated with the
component. They won't be visible for other components. _nghost
and _ngcontent
.
:host
Selector<app-course-card></app-course-card>
itself.
:host
selector. Note in the below example that the styles are now being applied to the component using a different
attribute selector that uses _nghost. Note that this attribute is different from the _ngcontent attribute that was being applied on the element and its children.
The child elements of the app-course-card do not have this attribute applied to them. Every component will have its own _nghost attribute. The point to note is
that even if there were multiple app-course-card components on the page, they would all use the same _nghost attribute.
::ng-deep
modifier::ng-deep
to our style, the part of the CSS selector
after ng-deep
is no longer going to be made specific to a particular component. The part before the ng-deep
selector is still going to
be made specific to that component by applying the special _ngcontent-c* attributes.
host-context
Selector - Theming Use Case.yellowTheme
selector in the course-card.component.css file, the style was made specific to the elements of the course-card component.
ng-deep
selector before the selector. But then the problem is that that would remove the component
specific selector from both the styles, inherently making it the same as adding the style to the global style sheet.
host-context
CSS modifier. The :host-context()
selector looks for a CSS class in any ancestor of the component host
element, up to the document root. For instance, in the following example, italicizes all text inside a component, but only if some ancestor element of the host element
has the CSS class active
. Link to Angular Docs.
@Component()
annotation that is present on the ts file, you can pass in an args encapsulation: ...
like here. The default value for this is ViewEncapsulation.Emulated
. ng run server
command. First error was '.' is not recognized as an internal or external command,
and second error was SyntaxError: missing ) after argument list
/ unterminated `s' command
. These were fixed by replacing the server
definition in the package.json file with "server": "node ./node_modules/ts-node/dist/bin.js -P ./server.tsconfig.json ./server.ts"
as mentioned here. localhost:9000/api/courses
is the path to access on the browser. HttpClient
that we are going to use to fetch data from the backend. HttpClient
, we can simply inject it into our constructor. The ngOnInit
method is where we should be adding the GET calls. async
Pipe - a Better way of passing Observable
data to the ViewObservable
that emits the requested data when the response is received. In the above example,
we were manually calling subscribe
on the Observable
. Note that calling subscribe()
is what triggers execution of the observable and
causes HttpClient to compose and send the HTTP request to the server. Each subscribe()
initiates a separate, independent execution of the observable. Subscribing
twice results in two HTTP requests. You should always unsubscribe from an observable when a component is
destroyed.async
pipe allows us to implicitly subscribe to this observable from the template. This allows us to write reactive components. It also has the additional
advantage that whenever the component is destroyed, the aysnc
pipe takes care of unsubscribing us from the Observable
which prevents memory leaks.
@Injectable
Decoratorng generate service services/courses
where services is the name of the folder in which we want the files to be
generated, and courses is the name that we want to assign to the service. The generated service looks something like this. The
@Injectable({providedIn: 'root'})
annotation means that this service can now be injected into the constructor of any class that we want, similar to how we were
injecting the HttpClient
in the above examples. The providedIn: 'root'
means that there should always be a single instance of the
CoursesService object available for injection (CoursesService should be a Singleton).
Observable<Course[]>
.
{...this.course}
and then in this new object change the description property by doing {...this.course, description}
.
You can try this in your browser if you are confused with this. In the image below is the case where the two names are different, just for clarity.
ng add @ng-bootstrap/ng-bootstrap
NgbModule
into the app.module.ts
and adding
the bootstrap.min.css
to the styles
in angular.json
file. ng new MyProjectName
cd MyProjectName
ng serve
ng generate service services/stocks
// Will create a new service in the src/app/services
folder ng generate component components/summary
// Will create a new component in the
src/app/component/summary folder npm -v
npm install -g typescript
npm install -g @angular/cli
ng help
ng new angular-hello-world
src/index.html
, you will see the <app-root></app-root>
tag. The app-root
tag is where our application will be rendered. app-root is a component that is defined by our Angular application. In Angular we can define our own HTML tags and give
them custom functionality. The app-root tag will be the “entry point” for our application on the page. ng serve
<select>
, <form>
, <video>
. But what if we wanted to
teach the browser some new tags, like <login>
to display a login bar. This is the basic idea behind components. We are creating components that are
basically custom tags and teaching the browser how to display them. In the below example, we will be creating our own custom tag:
app-hello-world
ng generate component hello-world
. This is what the generated component looks like:
@Input
decorator.
@Input()
allows a parent component to update data in the child component.
{{ }}
. For example, take note of the userName property
below. The userName property was defined in the file above as a property, and hence we are able to use it below in the template file for the class.
ng serve
, angular will look for the angular.json
file to find the entry point to the app. At a high level, the entire process
looks like this: angular.json
specifies a main
file, which in this case is the main.ts
file. main.ts
is the entry point for the application and bootstraps the entire application src/app/app.module.ts
where it find the AppModule
AppModule
specifies which component to use as the top-level component. In this case it is AppComponent
(src/app/app.component.ts) AppComponent
, in its templateUrl then defines the app.component.html
which finally contains our app-user-list component NgModule
which points to the component you want to load.
@NgModule
. The @NgModule defines four keys that are used. We will look at the keys one-by-one. declarations
: specifies the components that are defined in this module. You have to declare components in a NgModule before you can use them in your templates. When we use theng generate
to generate a new component, the CLI automatically adds the component to the list of declarations.
imports
: describes which dependencies this module has. Since we are creating a browser app, we have added the dependency asBrowserModule
.
Note that there are two similar keywords: import
and imports
. We put something in our NgModule's imports if we're going to be using it in our
templates or with dependency injection. providers
: providers is used for dependency injection. So to make a service available to be injected throughout our application, we will add it here.bootstrap
: tells Angular that when this module is used to bootstrap an app, we need to load theAppComponent
as the
top-level component.
<button/>
onClick
event, we do the above. When the button is clicked, it will call the addArticle method. The addArticle method will be defined in the
same class in which the templateUrl
is set as app.component.html. The addArticle function is defined as follows:
<input/>
elements, we can pass them as variables into the
addArticle() function on the button. <input name="title" id="title" #newtitle>
this markup tells Angular to bind this input
HTML element to the variable #newtitle.
The #newtitle syntax is called a resolve. newtitle is now an object that represents this input DOM element (specifically, the type is HTMLInputElement
).
Because newtitle is an object, that means we get the value of the input tag using newtitle.value. Similarly we add #newlink to the other
input
tag, so that we'll be able to extract the value from it as well. The objects that are returned from the resolve that we used on the input elements are then
passed into the function addArticle. @HostBinding
that I do not understand
@Decorator
templateUrl
or template
@Component
is called the Decorator that adds metadata to the class that follows it, which in this case is AppComponent
. The
@Component
annotation also specifies the selector
which tells Angular which element to match. By saying selector: app-root, we're
saying that in our HTML we want to match the app-root tag, that is, we’re defining a new tag that has new functionality whenever we use it. E.g. when we put this
in our HTML: <app-root>
, Angular will use the AppComponent component to implement the functionality. template
which
defines the view to render. AppComponent
class.
{{ }}
syntax is called template binding. It tells the view we want to use the value of the expression inside the brackets at this
location in our template. Note that the code inside the brackets is an expression. What it means is that we can do things like this:
[productList]="products"
Here we are saying that we want to read the value of the expression 'products' from the AppComponent and store that into the
productList variable that is defined in the ProductsList component. productList is being referred to in the text as 'attribute key' and products as
'instance property'. (onProductSelected)="productWasSelected($event)"
We're saying that we want to listen to the onProductSelected output from the ProductsList
component. (onProductSelected), the left-hand side is the name of the output we want to "listen" on. "productWasSelected", the right-hand side is the function we want
to call when something new is sent to this output. $event is a special variable here that represents the thing emitted on (i.e. sent to) the output.