using this repo for angular boilerplate
https://github.com/AngularClass/NG6-starter
But I found hard to access the dom inside angular component's $postLink phase.
Am I coding it wrong? Or do I have to use directive which is not optimal for one way binding?
about.component.js
import template from './about.html';
import controller from './about.controller';
import './about.scss';
let aboutComponent = {
restrict: 'E',
bindings: {},
template,
controller,
};
export default aboutComponent;
about.controller.js
class AboutController {
constructor() {
this.name = 'about';
this.$postLink = ($element) => {
console.log($element); // here is when I want to utilize the element!
}
}
}
export default AboutController;
about.js
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import aboutComponent from './about.component';
let aboutModule = angular.module('about', [
uiRouter
])
.config(($stateProvider) => {
"ngInject";
$stateProvider
.state('about', {
url: '/about',
component: 'about'
});
})
.component('about', aboutComponent)
.name;
export default aboutModule;
about.html
<section>
<navbar></navbar>
<h1>{{ $ctrl.name }}</h1>
<section>
About us.
</section>
</section>
You can't get $element(directive element) from $postLink lifecycle hook. You have to inject $element inside controller constructor to get directive element inside directive.
class AboutController {
static $inject = ["$element"]
constructor($element) {
this.name = 'about';
this.$element = $element;
}
// better move out $postLink outside controller.
$postLink = () => {
console.log($element);
}
}
export default AboutController;
Related
I just create the project AngularJs 1.7 and webpack 4 with ES6 class on component and module setup.
this is my app main module.
// Core Styles
import './styles/main.scss';
// Core Angular
import angular from 'angular';
import { AppComponent } from "./app.component";
import CommonModule from './modules/common/common.module';
import PackagesModule from './modules/packages/packages.module';
import PackagesService from "./services/packages.service";
// These all export the module name
// import ngAnimateModuleName from 'angular-animate';
const dependencies = [
// ngAnimateModuleName
CommonModule.name,
PackagesModule.name
];
angular.module('app', dependencies)
.component('appComponent', AppComponent)
.service('PackagesService', ['$http', PackagesService]);
this is my js component file.
import './app.component.scss';
export const AppComponent = {
template: require('./app.component.html'),
controller: [
'PackagesService',
class AppController {
constructor (PackagesService) {
this.test = 11;
}
}]
};
But seems like test variable is not available inside the template file.
<header-component></header-component>
<div class="container">
<div class="row">
<div class="col-6">
<div class="packages-name-box">
<h1 class="homepage-title">Packages Name {{ test }}</h1>
<packages-list-group pages-data="pagesPackageData"></packages-list-group>
</div>
</div>
</div>
</div>
And this is HTML LOADER inside webpack.confing
{
// HTML LOADER
// Reference: https://github.com/webpack/raw-loader
// Allow loading html through js
test: /\.html$/,
loader: 'raw-loader'
}
Am i missing something why this.test is not display anything inside the template?
Thanks for your help!
Since you are not using ControllerAs syntax, you should assign the value to the $scope variable,
$scope.test = 11;
you need to inject $scope as
'PackagesService','$scope',
class AppController {
constructor (PackagesService,$scope) {
$scope.test = 11;
}
On angularjs components, all variables declared with this can be acessed by the object $ctrl on the scope. You should use:
<h1 class="homepage-title">Packages Name {{ $ctrl.test }}</h1>
you can change the object name to whatever you want on controllerAs like this: (but it's $ctrl by default)
export const AppComponent = {
template: require('./app.component.html'),
controllerAs: 'vm',
controller: [
'PackagesService',
class AppController {
constructor (PackagesService) {
this.test = 11;
}
}]
};
see angularjs component document: https://docs.angularjs.org/guide/component
Example I have 1 component call 'people' at html like this and its works
<html>
<people data="$ctrl.data" isAlive="$ctrl.status"></people>
</html>
But if i want to call people component in another component how ?
I try this solution but it is not working
import templateUrl from './main.html';
export const mainComponent = {
bindings: {},
templateUrl,
controller: class MainComponent {
constructor() {
'ngInject';
}
$onInit() {
this.data = { name : test }
this.status = 'ALIVE'
this.people = `<people data="${this.data}"
isAlive="${this.status}">
</people>`
console.log(this.people) //this should return a html template with
// complete data
}
}
};
Please advise
Why would you want to generate people component template in your controller ? You can just add the component itself in your secondary's component template.
E.g.
angular.module('app',[])
.component('people', {
template:'<div style="color:green;">We are people</div>'
})
.component('alien',{
template:`<div style="color:red">We are alien but we have people</div>
<people></people>`
})
Update
You can just pass data to your component right after you get it from your external lib.
.component('people', {
template: '<div style="color:green;">We are people, who got some {{$ctrl.data}}</div>',
bindings: {
data: '<'
}
})
.component('alien', {
template: `<div style="color:red">We are alien but we have people</div>
<people data="$ctrl.data.async"></people>`,
controller: function($timeout) {
$ctrl = this;
$ctrl.data = {
async: null
}
function getAsyncData() {
$timeout(() => {
$ctrl.data.async = "Fooooo"
}, 1000);
};
getAsyncData();
}
})
Here's a working fiddle
I've html structure like this :
Parent Component where both Comp1 and Comp2 resides :
now in comp1 I've some elements if that changes then I've to reflect values in comp2 but there's no connection between them.
Comp1 :
import { Component } from '#angular/core';
import { Comp2Component } from 'comp2.component';
#Component({
selector: 'comp1',
templateUrl: './comp1.html'
})
export class Comp1Component {
sortBy(value)
{
this.FeedSortBy = value;
this.SortByLabel = this.SortByLabelList[value];
Comp2Component.filterActivities();
}
}
Comp2
import { Component } from '#angular/core';
#Component({
selector: 'comp2',
templateUrl: './comp2.html'
})
export class Comp2Component {
filterActivities()
{
//call this function on comp1 sort function call so it will update value of comp2
}
}
As per Rahul and Sajit's answer I try using with EventEmitter and change my structure to parent child :
In my parent component I use :
import { Component,EventEmitter, Input, Output, OnInit } from '#angular/core';
import { FeedsComponent } from '../feeds/feeds.component';
#Component({
selector: 'my-desk',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './my-desk.component.html'
})
export class MyDeskComponent {
#Output() FeedSortBy = new EventEmitter<string>();
sortBy(value)
{
this.FeedSortBy.emit(value);
}
}
and in my child component I use :
import { Component, OnInit, Input, Output } from '#angular/core';
import { DataService } from '../data.service';
declare var $: any;
#Component({
selector: 'feeds',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './feeds.component.html'
})
export class FeedsComponent {
constructor(private dataService:DataService)
{
}
#Input() FeedSortBy:number = 2;
}
Child component HTML :
{{FeedSortBy}}
But it always output 2 it doesn't change can I get any trigger as well to know if value is change so I call function there
You cannot do that, There are two possible ways you could achieve this,
use angular service to pass the data between two components
use Event Emitters to pass the value among the components.
You can call method of another component from a different component but it will not update the value of the calling component without some tweaking like
Event Emitters if they have a parent child relationship or Shared Services or using ngrx redux pattern
How to Call a different component method be like
Component1
test(){
console.log("Test");
}
Component 2
working(){
let component = new Component1();
component.test();
}
Now to update the value in component 2 you might have to use any of the above.
For Event Emitters follow this link
For Shared services follow this link
For ngrx follow this link
In my app-root component I have router-outlet in container with some styles.
I have route:
{
path: 'some-path',
component: ChildContainer,
data: { variable:'variable' },
}
And I can to get variable in ChildContainer, but I need it in AppRoot. So, from documentation I can get it from child, but if I do this in AppRoot constructor:
const state: RouterState = router.routerState;
const root: ActivatedRoute = state.root;
const child = root.firstChild;
and console.log(root, child) - child is null, and root contains correct child (invoke property getter).
So, how can I get variable in AppRoot?
You may tap into activate event to get reference of instantiated component inside the router outlet.
Check This SO question
#Component({
selector: 'my-app',
template: `<h3 class="title">Basic Angular 2</h3>
<router-outlet (activate)="onActivate($event)" ></router-outlet>
`
})
export class AppComponent {
constructor(){}
onActivate(componentRef){
componentRef.sayhello();
}
}
#Component({
selector: 'my-app',
template: `<h3 class="title">Dashboard</h3>
`
})
export class DashboardComponent {
constructor(){}
sayhello(){
console.log('hello!!');
}
}
Here is the Plunker!!
Update
expose ActivatedRoute as a public property and once you have the routed component reference, subscribe to data,
onActivate(componentRef){
componentRef.route.data.subsribe(data => {
console.log(data);
});
}
Background
Suppose I have some parent component, call it MatchList, that presents a list of Hero objects, among other things. Each Hero object has properties that are shown in some table. Now suppose I also have a button for each Hero that updates the route, loads a new view, and shows more details.
Before
http://heroic.com/match-list
After
http://heroic.com/hero-84
Problem
My problem essential is this: I want to call the router's navigate() method from a button in my MatchList template, but I receive the following error when I attempt to do so:
EXCEPTION: Error during evaluation of "click"BrowserDomAdapter.logError # ...
angular2.dev.js:21835 ORIGINAL EXCEPTION: TypeError: l_context.setPath is not a function...
angular2.dev.js:21835 TypeError: l_context.setPath is not a function at ...
In other words It looks like I cannot reference the parent component's router methods in the child template.
So, what is the correct and best way in Angular 2 for a child component access the methods of the parent component ( or 'context')?
I'd prefer if the solution was something cleaner than
class parent {
child: Child;
constructor(...) {
...
this.child.parent = this;
}
}
Sample Code
EDIT
I changed my template button to
(^click)="setPath(match.match_id)"
I am not longer receiving an error message, but nothing happens - I don't even get a console log confirming the click.
Snippets of what I have so far.
//Parent
#Component({
selector: 'dota-app',
directives: [Home, MatchesView, ROUTER_DIRECTIVES],
templateUrl: 'AppView.html'
})
#RouteConfig([
{ path: '/', component: Home, as: 'Home' },
{ path: '/matches', component: MatchesView, as: 'Matches' },
{ path: '/match-details', component: MatchDetailsView, as: 'MatchDetails'}
])
export class RootDotaComponent {
router: Router;
constructor(router: Router) {
this.router = router;
}
public setPath(linkParams: any[]|string): void {
if (typeof linkParams === "string")
linkParams = [linkParams];
this.router.navigate(<any[]>linkParams);
}
}
}
//Child
#Component({
selector: 'matches-view',
providers: [DotaRestDao],
})
#View({
templateUrl: './components/MatchesView/MatchesView.html',
directives: [CORE_DIRECTIVES]
})
export class MatchesView {
public result;
private dataService: DotaRestDao;
constructor(dataService: DotaRestDao) {
this.result = { matches: [] };
this.dataService = dataService;
this.dataService.getData({
baseUrl: DotaRestDao.MATCH_HISTORY_BASE
}).subscribe(
res => this.result = res.result,
err => console.log("something wrongable", err),
() => console.log('completed')
);
}
}
//Template
<table class="table">
...
<button (click)="setPath(match.match_id)">Match Detail Route</button>
</table>
In the context of this question, namely calling a parent router, the answer, it turns out, is trivial. See this plunker for details.
The main takeaway is that giving a router to a child component a la
class ChildComponent {
constructor(router: Router) {
...
}
}
does not create a new router, it merely extends the existing router of the parent component. Thus, the need to a reference to the parent object is obviated. Just call the methods of the childRouter and everything works as expected.