I am following the component-relative paths angular documentation.
Per the instructions, I am keeping my component ts and component html in the same directory. For the purpose of this question, that directory is /app/components/first
My component ts contains the following code, and is named First.component.ts
import {Component, Injectable} from '#angular/core';
import {MyService} from "../../service/MyService";
import {ValueObj} from "../../value/ValueObj";
#Component({
moduleId: module.id,
selector: 'relative-path',
templateUrl: 'First.component.html',
providers: [MyService]
})
#Injectable()
export class FirstComponent {
public valueObjs: ValueObj[];
constructor(private _myService: MyService) {}
getAllItems(): void {
this._myService.getAllValueObjs().subscribe(
data => this.valueObjs= data, error=> console.log(error),
() => console.log("getting complete"));
}
}
My First.component.html contains the following code:
<form (ngSubmit)="getAllItems()">
<label>Value Objects</label>
<button type="submit">Search</button>
</form>
<hr>
<p style="color:red">{{error}}</p>
<h1>All Objects</h1>
<div *ngFor="let valueObj of valueObjs">{{valueObj.name}}</div>
I have looked at this solution, The selector "my-app" did not match any elements , however, I do not find it applicable. My main.ts file only contains this:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
// start the application
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
and has no reference to BrowserModule. I also looked at why getting the error "The selector did not match any elements"? , however, I have no boot.ts file to compare my values.
Lastly, I have looked at Angular2 CLI build EXCEPTION : The selector "app-root" did not match any elements , however, I have no reference to angular-universal in my packaging.json.
Any idea as to what I am doing wrong?
I just had the same issue, the example is a bit confusing. The line:
selector: 'relative-path',
is just an example name of your component, it is not a command to use relative paths.
Simply adding the moduleId line will make relative paths work, so you can leave your selector as "first-component" or whatever you had before.
The error message is actually trying to say that the saying that the selector you have now ("relative-path") does not match what is in the index.html file. This only happens when you change the component that is in the bootstrap entry in app.module. If you had the same issue with a non-bootstrap module, you get a much clearer error message.
Related
I want to "insert" task.component's view into my main app.component.html (root template) as follows:
(app.module.ts)
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { TaskComponent } from './task/task.component';
#NgModule({
declarations: [
AppComponent,
TaskComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
(app.component.ts)
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'planner';
}
(task.component.ts)
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-task',
templateUrl: './task.component.html',
styleUrls: ['./task.component.css']
})
export class TaskComponent implements OnInit {
constructor() { }
ngOnInit() {
console.log("Task comonent initialized!");
}
}
(app.component.html)
<!--The content below is only a placeholder and can be replaced.-->
<app-task></app-task>
<div style="text-align:center">
<h1>
Welcome to {{ title + 1 }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>
So despite the fact that I have linked the task.component to the module I do not have access to its selector. Did I forget some import/export statements. Is that in fact a misunderstanding of angular's architecture/principle of structure?
The file task.component.html only consists of
<p>
task works!
</p>
Edit:
I also received the following error from the client's console:
[
What exactly shows in the place of the selector <app-task></app-task> that you added in app.component.html ?? What error in the console. This selector should work and be rendered to TaskComponent html view !!!!
To add a New Component to the Module that you're working on (which in your case is the AppModule), you first create a component(just use AngularCLI to do this by running the command ng g c task), and Angular CLI automatically adds it to your AppModule's declarations array. Then you can simply add the selector tag to your app.component.html to load that up
Just add <app-task></app-task> to your app.component.html
UPDATE
In some cases, even when you do everything properly, Angular doesn't recognise the Component that was added recently. Try breaking your local service by Ctrl + C on Windows or Cmd + C on Mac. And then run ng serve to serve up the App again. This generally happens when a Component is Added while the Server is running.
Here's a Sample StackBlitz for your ref just to cross check if you missed something.
I don't know what did you mean for linking components?
Following the structure that you shared it's working if you want see something, you must create the task-component.html because doesn't exist.
And the idea a component is that each component must has its logical indipendent to work.
And ways to share resources is using EventEmitters or binding properties.
This is the sceenshot that I saw and I can see the component created.
In fact restarting the server with ng serve did not solve the problem and therefore was not responsible for the missing interpolation of the component's template.
Rebuilding the app was the only (and ugliest) option for me which caused additionally work of course.
Delete all files of your project (all files in the layer in which amongst others node_modules is placed)
Setting up the project with ng new (...)
Building clean components with terminal commands (ng g c (...) / ng generate component (...))
Try re-running ng serve after stopping it. Hope you added component using Angular-Cli command -
ng generate component task
I'm having a bit of trouble I cant seem to figure out why my #ViewChild isnt working..
Basically I want to call a function in one component from another component so in my sidebar component I have a function called sendData() and I want to be able to call that from a button click in my header component so what Ive done is..
Sidebar component
import { Component, OnInit, Input, OnChanges, SimpleChanges, AfterViewInit } from '#angular/core';
import * as _ from 'lodash';
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent implements OnInit, OnChanges {
constructor(
private contenfulService: ContentfulService,
private userService: UserService
) { }
ngOnInit() {
}
sendData(){
...do something
}
}
header.component.ts
import { Component, OnInit, Input, ViewChild, EventEmitter } from '#angular/core';
import { UserService } from '../../../user.service';
import { SidebarComponent } from '../sidebar/sidebar.component';
#Component({
selector: 'app-program-header',
templateUrl: './program-header.component.html',
styleUrls: ['./program-header.component.scss']
})
export class ProgramHeaderComponent implements OnInit {
#ViewChild(SidebarComponent) sidebar;
constructor() { }
ngOnInit() {
}
}
header.component.html
<div (click)="sidebar.sendData()"></div>
but it isnt working Im getting this error in the console...
ERROR TypeError: Cannot read property 'sendData' of undefined
I have removed code for brevity, so please let me know if there is more information you need
Im not sure what the problem is?
EDIT
Or if anyone knows another way to call a function from a seperate component let me know
Any help would be appreciated!
Thanks!
ViewChild is expected to be used to get target element from current component's view(template) which matches the selector.
But according to your comment above, it seems there is no app-sidebar placed in your header.component.html, so ViewChild is not able to get a valid element which results in your current error.
The solution should be place app-sidebar at least once.
<app-sidebar></app-sidebar>
<div (click)="sidebar.sendData()"></div>
If header and sidebar components are siblings, you can not pass data between them directly. Take a look at ‘Output’ and ‘Input’ from angular. Alternatively, you can use a Service to pass data between components. Check out services and observables.
<parent>
<app-program-header>
</app-program-header>
<app-sidebar>
</app-sidebar>
</parent>
Just as an addition to this conversation, I have been squirreled off on a couple of occasions chasing what seemed to be the lack of a functioning #ViewChild when the cause was the #ViewChild referencing a component in a module that was not being imported. The "cannot read property of undefined" can become extremely misleading and may not be related to #ViewChild at all; be sure to check your imports first.
In my case - it was the import from "#shared" which has caused this issue. You have to pay attention, that the component you are using the #ViewChild component reference is not in the same module with same shorthand path. If it is, import the #ViewChild component from
'shared/components/sidebar/sidebar.component'
and not from '#shared'
https://angular.io/guide/architecture#services
I'm following the docs on angular.io to inject dependencies like services, etc. I did everything they said and when I try to run it, the console keeps telling me:
Uncaught ReferenceError: LedgerService is not defined
I am doing nothing crazy except creating a simple component with a service where both constructors have console.log commands (constructors in both the component and service). I've done everything Angular says to do in their 2 paragraphs that details this feature of Angular.
The component itself is being injected into the main app module (with the service being injected into the component) and both the component and service were created with the Angular CLI. So there isn't much I've even done at all minus trying to inject the service. So I'm not sure where it is going wrong but it is definitely not working and just shows a blank page (when it previously had basic content by default).
I created both units, tried to specify providers in both the app.module and the component.ts file and neither works and yields the same error--when Angular claims either could work. I've also specified it as a private service within the constructor of the component.ts file.
Everything I've seen relating to this is always for Angular 1 or 2. Neither of which are even remotely similar to Angular 4/5.
If you really want to see this code, fine but it's literally just framework and nothing else:
bookkeeper.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-bookkeeper',
templateUrl: './bookkeeper.component.html',
styleUrls: ['./bookkeeper.component.css'],
providers: [LedgerServiceService]
})
export class BookkeeperComponent implements OnInit {
constructor(private service: LedgerServiceService) { }
ngOnInit() {
console.log("Ledger component works!");
}
}
app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { InterfaceComponent } from './interface/interface.component';
import { BookkeeperComponent } from './bookkeeper/bookkeeper.component';
#NgModule({
declarations: [
AppComponent,
InterfaceComponent,
BookkeeperComponent
],
imports: [
BrowserModule
],
providers: [
LedgerServiceService
],
bootstrap: [AppComponent]
})
export class AppModule { }
ledger-service.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class LedgerServiceService {
constructor() {
console.log("wtf");
}
}
LedgerService is actually called LedgerServiceService because I initially created LedgerService manually and then tried to use the AngularCLI to generate a service and named it LedgerService and it created a service called LedgerServiceService. Naming is not what is wrong. I only initially called it simply LedgerService because I figured it would be confusing.
Your examples are missing the import.
Anywhere we use a custom type, we also need to import that type.
For that reason, in both the module and component you will need to add:
import { LedgerServiceService } from './your-path-here'
You can see this in the examples they give on https://angular.io/guide/dependency-injection
Basically I wanted to load component html specific script file, so that script I'm going to put script file reference inside component html itself, I'm seeing that inner script file has been ignored while rendering component html on page.
Component
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'test.html'
})
export class AppComponent { }
test.html
<div>
<h1>My First Angular 2 App</h1>
</div>
<script src="test.js"></script>
Above is my code what I tried & I already have test.js there in place.
Plunkr Here
Is there any way to load component specific javascript file with component OR with its html?
Working Plunker
Security
It looks like Angular takes out script tags from Html templates.
From the Angular Docs:
It removes the <script> tag but keeps safe content, such as the text content of the <script> tag
Angular provides methods to bypass security, but for your use case it looks like a service would be helpful.
Services
The preferred way to include your own custom script in your component from a separate dedicated file would be to create a service.
I took the code from your Plunker's script.js file and put it into a service like this:
// File: app/test.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class TestService {
testFunction() {
console.log('Test');
}
}
Then I imported the service and called the custom code like this:
// File: app/app.component.ts
import { Component, OnInit } from '#angular/core';
import { TestService } from './test.service';
#Component({
selector: 'my-app',
templateUrl: 'test.html',
providers: [TestService]
})
export class AppComponent implements OnInit {
constructor(private testService: TestService) {}
ngOnInit() {
this.testService.testFunction();
}
}
Lifecycle hooks
If you want to call your service's custom code at a specific point you can take advantage of lifecycle hooks. For example you can call your code using the ngAfterViewInit() instead of ngOnInit() if you want to wait until the view has loaded.
I have an app that is structured like this.
<app>
<header>
<component-a></component-a>
</header>
<div>
<router-outlet></router-outlet>
<component-b></component-b> <!-- loaded through router -->
</div>
</app>
In component-b some data is retrieved and later set to a new object. For example, something like this..
{
infoThatComponentANeeds : true,
someMoreInfoAddedFromCompB : []
}
This info is needed for the template in component-a. At first I tried to pass the info up to component-a via #Outuput and eventEmitter using a custom event. But I was unable to get it to bubble up. I suspect that has to do with it being loaded through the router. So now I am trying to share the data between the two components using a shared service.
My Service So Far:
import {Injectable} from 'angular2/core';
#Injectable()
export class SharedService
{
public spec:Spec= null;
public getSpec()
{
return this.spec;
}
public setSpec(spec:Spec)
{
this.spec = spec;
}
}
This is how I am trying to use it in component-a:
ngDoCheck()
{
if(this._sharedService.spec)
{
this.spec= this._sharedService.getSpec();
}
}
The Problem:
After the spec is set in component-b and ngDoCheck from component-a checks to see if the spec has been set. It comes back as undefined so the getSpec() function does not run, and no spec is returned. So I am not sure what I am missing, or why it would still be undefined in my SharedService after it has been set. Should the shared service keep a reference to what was set? Or am I completely misunderstanding this concept?
I have also explored ways of sharing this data via promises/observables. However I have not had any luck with that either. And the majority of the examples I have found use HTTP, which I really do not need at this point.
Update:
Here is some more info.
Boot.ts
import {bootstrap} from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import {
ROUTER_PROVIDERS,
APP_BASE_HREF,
Location,
LocationStrategy,
HashLocationStrategy,
PathLocationStrategy
} from 'angular2/router';
import {AppComponent} from './components/app.component';
import {SharedService} from './services/shared.service';
bootstrap(<any>AppComponent, [
ROUTER_PROVIDERS,
SharedService,
provide(LocationStrategy, {useClass: PathLocationStrategy})
]);
AppComponent.ts
import {AfterViewInit, Component, OnInit} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {ComponentA} from './componentA';
#Component({
selector : 'body',
directives : [ComponentA, ROUTER_DIRECTIVES],
template : `
<app>
<header>
<component-a></component-a>
</header>
<div>
<router-outlet></router-outlet>
</div>
</app>
`
})
#RouteConfig([
{path: '/:appName/', name: 'ComponentB', component: ComponentB}
])
export class AppComponent
{
constructor(){}
}
Update 2:
Here is a plunker I created to try to isolate the issue. I removed the router stuff and simplified the whole thing. I am still seeing the same result..
https://plnkr.co/edit/kx6FWWGS1i04bH5J9DXM?p=preview
Watch the console.log()
Fixed:
This was the key.
Be sure to remove configurations in the providers attribute of your
two components.
Perhaps your service isn't actually shared. I mean you could have two instances according to the way you configured providers.
To be sure, just add the service when bootstrapping your application:
bootstrap(AppComponent, [ SharedService ]);
Be sure to remove configurations in the providers attribute of your two components.
If you're interested in hierarchical injectors of Angular2 (the concept behind this), you could have a look at this question:
What's the best way to inject one service into another in angular 2 (Beta)?