I have to click on the following menu "Text View" tab (mat-tab-link) twice to get the router-outlet to become active and underline the tab. I know this because of the documents.component.html content for the radio buttons, in it does not become active, unless I click twice on the Text View tab. Then the "Option 4" button shows active, and the tab underlines.
documents-pane.component.html
<nav mat-tab-nav-bar aria-label="documentsTabs">
<a class="navbar-brand" [routerLink]="['/customApp']">CustomApp</a>
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.path"
routerLinkActive #rla="routerLinkActive"
[active]="rla.isActive"
skipLocationChange>
{{link.label}}
</a>
</nav>
<router-outlet></router-outlet>
documents-pane.component.ts
import { Component, OnInit } from '#angular/core';
import { CustomAppDocumentsComponent } from './documents/documents.component';
import { CustomAppGridComponent } from './grid/grid.component';
import { CustomAppSearchComponent } from './customApp-search/customApp-search.component';
#Component({
selector: 'app-documents-pane',
templateUrl: './documents-pane.component.html',
styleUrls: ['./documents-pane.component.scss'],
})
export class CustomAppDocumentsPaneComponent implements OnInit {
constructor() { }
ngOnInit() {
}
navLinks = [
{path: 'documents', label: 'Text View'},
{path: 'grid', label: 'Grid View'},
{path: 'customApp-search', label: 'Search Terms'},
]
}
app-routing.module.ts
...
import { CustomAppDetailComponent } from './customApp/customApp-detail.component';
import { CustomAppBaseComponent } from './customApp/documents-pane/customApp-base/customApp-base.component';
import { CustomAppDocumentsComponent } from './customApp/documents-pane/documents/documents.component';
import { CustomAppGridComponent } from './customApp/documents-pane/grid/grid.component'
import { CustomAppSearchComponent } from './customApp/documents-pane/customApp-search/customApp-search.component'
...
...
const routes: Routes = [
{
path: 'customApp',
component: CustomAppDetailComponent,
children: [
{
path: '',
component: CustomAppBaseComponent,
},
{
path: 'customApp-base',
component: CustomAppBaseComponent,
},
{
path: 'documents',
component: CustomAppDocumentsComponent,
},
{
path: 'grid',
component: CustomAppGridComponent,
},
{
path: 'customApp-search',
component: CustomAppSearchComponent,
},
]
}
...
documents.component.html
<div style='float: left; width: 75%;'>
<mat-radio-group formControlName="testEntryOption">
<mat-radio-button class="material-radio" value="opt1">Option 1</mat-radio-button>
<mat-radio-button class="material-radio" value="opt2">Option 2</mat-radio-button>
<mat-radio-button class="material-radio" value="opt3">Option 3</mat-radio-button>
<mat-radio-button class="material-radio" value="opt4" [checked]="true">Option 4</mat-radio-button>
</mat-radio-group>
</div>
<div style='float: left; width: 25%;'>
<mat-button-toggle-group name="buttonSelection">
<mat-button-toggle value="button1">Button 1</mat-button-toggle>
<mat-button-toggle value="button2">Button 2</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div>
documents works!
</div>
I have tried several other code implementations, even a bootstrap menu bar, with no luck. Any input is greatly appreciated. What are my options?
Related
I need to change the content and UI language of the CKEditor5 when I click a button. Here's a screenshot of the app. On each button click, the config is updated with the new language selected. But the CKEditor remains with the same configuration when it has been initialized. How can we modify the content and UI of the editor dynamically using Angular or TypeScript?
Note: I'm using Angular 5, Angular CLI 1.7.4. And also I'm using CKEditor5 version 1.2.3
Here's the .ts component code:
import { Component, OnInit } from '#angular/core'
import { ChangeEvent } from '#ckeditor/ckeditor5-angular'
import * as ClassicEditor from '#ckeditor/ckeditor5-build-classic'
#Component({
selector: 'another-text-editor',
templateUrl: './another-text-editor.component.html',
styleUrls: ['./another-text-editor.component.css']
})
export class TextEditorComponent implements OnInit{
languages = [
{ id: 'fr', label: 'French' },
{ id: 'de', label: 'German' },
{ id: 'ar', label: 'Arabic' }
]
public Editor = ClassicEditor
config = {
toolbar: ['bold', 'italic'],
language: { ui: 'fr', content: 'fr' }
}
constructor () {}
ngOnInit () {}
changeConfig (lang) {
this.config = {
...this.config,
language: {
ui: lang,
content: lang
}
}
}
public onChange ({editor}: ChangeEvent) {
console.log(editor.getData())
}
}
And here's the HTML code for the component:
<div class="container pt-4">
<ckeditor [editor]="Editor" id="editor" tagName="textarea" [config]="config" [(ngModel)]="text" (change)="onChange($event)"></ckeditor>
<div class="row justify-content-start align-items-center mt-4">
<div class="col-auto" *ngFor="let lang of languages">
<button class="btn btn-primary" (click)="changeConfig(lang)">
Click for {{lang.label}}
</button>
</div>
</div>
</div>
I've added the translation files in .angular-cli.json:
"scripts": [
"../node_modules/#ckeditor/ckeditor5-build-classic/build/translations/de.js",
"../node_modules/#ckeditor/ckeditor5-build-classic/build/translations/fr.js",
"../node_modules/#ckeditor/ckeditor5-build-classic/build/translations/ar.js"
],
I am trying to use scrollIntoView when my page is loaded and when a user clicks on one of the menu buttons.
Therefore, I am subscribing to the route params in ngOnInit (Also tried in ngAfterViewInit) and inside there I activate "scrollIntoView()".
The issue is that it is not working on the First loading of the page (if the client enter direcly to www.website.co.il/home/componentName instead of www.website.co.il/home) but only if I change my route after the page is loaded.
app-routing.module.ts
{ path: '', component: HomeComponent },
{ path: 'home/:section', component: HomeComponent },
{ path: 'home', component: HomeComponent }
navbar.component.html
<li
class="menuItem"
*ngFor="let menuItem of (menuItems$ | async).menuItems; trackBy: trackByFunction">
<a
class="menuItem__link"
[routerLink]="['home', menuItem.link]">{{ menuItem.name }}</a>
</li>
our-services.component.ts
this.route.params.subscribe(params => {
if(params.section === COMP_LINK_NAME) {
this.container.nativeElement.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
}
});
}
COMP_LINK_NAME is const parameter of our-services component
I have an application which looks like on the image below.I used mattabs inside nav links.
this.navLinks = [
{
label: 'Preview',
link: './1',
index: 0
}, {
label: 'Tuning',
link: './tabtest2',
index: 1
}, {
label: 'Payment',
link: './tabtest3',
index: 2
},
];
in this code part when I press preview it shows me the 2nd element on the list(array).Thats exactly how I want it to work but it should be controlled dynamically.I made string concatanation and changed with that link on the preview tab but URL isnt recognizable.My concatanation is exactly same like '.1' on the link property
Here's what I tried below
TS File
import { Component, OnInit } from '#angular/core';
import { Car } from './car.model';
import { CarService } from './car.sevice';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { Subject } from 'rxjs';
#Component({
selector: 'app-cars',
templateUrl: './cars.component.html',
styleUrls: ['./cars.component.css']
})
export class CarsComponent implements OnInit {
navLinks: any[];
public href: string = "";
activeLinkIndex = -1;
mySubject;
ngOnInit(): void {
this.href = this.router.url;
console.log(this.router.url);
this.router.events.subscribe((res) => {
this.activeLinkIndex = this.navLinks.indexOf(this.navLinks.find(tab => tab.link === '.' + this.router.url));
});
this.mySubject=this.carService.carrierSubject.subscribe(value=>
{
this.id=value;
let numid=this.id.toString();
this.newString="./".concat(numid);
console.log(this.newString);
})
}
newString:string='';
id:number;
car:Car;
constructor(private carService:CarService,private route: ActivatedRoute,private router: Router) {
this.navLinks = [
{
label: 'Preview',
link: '.1',
index: 0
}, {
label: 'Tuning',
link: './tabtest2',
index: 1
}, {
label: 'Payment',
link: './tabtest3',
index: 2
},
];
}
onTuning()
{
this.router.navigate(['tuning'], {relativeTo: this.route});
}
}
HTML
<div class="row">
<div class="col-md-5">
<app-car-list></app-car-list>
</div>
<div class="col-md-7">
<nav mat-tab-nav-bar>
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.link"
routerLinkActive #rla="routerLinkActive"
[active]="rla.isActive">
{{link.label}}
</a>
</nav>
<router-outlet></router-outlet>
</div>
</div>
I'm new to angular and wanted to know how can i manipulate a dom element, select tag specifically, when a user clicks a button.
This is the code i'm using:
parent component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-add-recipe',
templateUrl: './add-recipe.component.html',
styleUrls: ['./add-recipe.component.css']
})
export class AddRecipeComponent implements OnInit {
units = [
{ id: 1, name: "test" },
{ id: 2, name: "test1" },
{ id: 3, name: "test2" },
{ id: 4, name: "test3" }
];
constructor() {}
ngOnInit() {
}
addIngredient(object) {
// doing stuff
}
}
parent view:
<input #ingredientName type="text" class="form-control" placeholder="name">
<input #ingredientQty type="text" class="form-control" placeholder="qty">
<app-select-units [units]="units"></app-select-units>
<button type="button" class="btn btn-outline-primary" (click)="addIngredient({ name: ingredientName.value, quantity: ingredientQty.value, unit: chosenUnit }); ingredientName.value=''; ingredientQty.value=''; ingredientUnit.value=''">add</button>
child component:
import { Component, OnInit, Input, ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-select-units',
templateUrl: './select-units.component.html',
styleUrls: ['./select-units.component.css']
})
export class SelectUnitsComponent implements OnInit {
#Input() units: string[];
#Input() selectedUnit: string;
constructor() { }
ngOnInit() {
}
}
child view:
<select [(ngModel)]="chosenUnit" #ingredientUnit class="form-control" name="selectUnit">
<option value="" [attr.selected]="!selectedUnit ? true : null" disabled>select</option>
<option *ngFor="let unit of units" value="{{ unit.id }}" [attr.selected]="selectedUnit == unit.id ? true : null">{{ unit.name }}
</option>
</select>
when the user clicks the button on the parent component, it calls it's "addIngredient" method, and clearing the two inputs.
i also try to clear the select to the first option (i know it's not possible this way).
Is there any way to bind the select to the parent component and clear it once the button click?
I have developed a custom tab component using Angular 2 which is working fine.
This is the usage of it.
<us-tab-verticle>
<vtab-content [tabTitle]="'Basic Information'"><basic-info> </basic-info></vtab-content>
<vtab-content [tabTitle]="'VAT Settings'"><vat-settings> </vat-settings></vtab-content>
<vtab-content [tabTitle]= "'Economy Settings'"><economy-settings> </economy-settings></vtab-content>
<vtab-content [tabTitle]="'Access Profiles'"><access-profiles> </access-profiles></vtab-content>
</us-tab-verticle>
The problem is all the tab components are loading when the view load.
My tab implementation is as follows.
us-tab-verticle.component.html
<div class="v-tabs">
<div class="v-tabs-col v-tabs-left">
<ul class="nav nav-v-tabs flex-column" role="tablist">
<li class="nav-v-item" *ngFor="let tab of tabs" (click)="selectTab(tab)">
<a [class.active]="tab.active" class="nav-v-link" data-toggle="tab" href="#" role="tab">{{tab.title}}</a>
</li>
</ul>
</div>
<div class="v-tabs-col v-tabs-fill">
<div class="v-tabs-content">
<div>
<ng-content></ng-content>
</div>
</div>
</div>
us-tab-verticle.component.ts
import { Component, ContentChildren, QueryList, AfterContentInit } from '#angular/core';
import { VtabContentComponent } from './vtab-content/vtab-content.component';
#Component({
selector: 'us-tab-verticle',
templateUrl: './us-tab-verticle.component.html',
styleUrls: ['./us-tab-verticle.component.scss']
})
export class UsTabVerticleComponent implements AfterContentInit {
#ContentChildren(VtabContentComponent) tabs: QueryList<VtabContentComponent>;
// contentChildren are set
ngAfterContentInit() {
// get all active tabs
const activeTabs = this.tabs.filter((tab) => tab.active);
// if there is no active tab set, activate the first
if (activeTabs.length === 0) {
this.selectTab(this.tabs.first);
}
}
selectTab(tab: VtabContentComponent) {
// deactivate all tabs
this.tabs.toArray().forEach(tab => tab.active = false);
// activate the tab the user has clicked on.
tab.active = true;
}
}
vtab-content.component.html
<div class="tab-content v-tab-content align-content-stretch">
<div [hidden]="!active" class="tab-pane active" role="tabpanel">
<ng-content></ng-content>
</div>
</div>
vtab-content.component.ts
import { Component, Input } from '#angular/core';
import { NgComponentOutlet } from '#angular/common';
#Component({
selector: 'vtab-content',
templateUrl: './vtab-content.component.html',
styleUrls: ['./vtab-content.component.scss']
})
export class VtabContentComponent {
#Input('tabTitle') title: string;
#Input() active = false;
}
I need to load each component when I click the header of the each tabs.
I sow that NgComponentOutlet can use to this kind of situations. But could not get any idea how to implement.
There are many solutions, if you want to improve your solution i suggest the usage of *ngIf. But notice that you destroy and create a new Component every time the *ngIf state changes.
I suggest you to take a look at RouterModule with the use of children attribue.
A little sample : (not sure you can make it work right away but I use similar stuff in my app)
// Router.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const routes: Routes = [
{
path: '',
children: [
{ path: 'tab1', component: Tab1Component },
{ path: 'tab2', component: Tab2Component },
]
},
{ path: '**', redirectTo: '' } // modify for default tab?
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routedComponents = [Tab1Component, Tab2Component];
// AppModule.ts
imports: [AppRoutingModule]
declarations: [
routedComponents
],
// main.html
<app-header>
<us-tab-verticle>
<div class="tab" [routerLink]="['/tab1']"><a>
<div class="tab" [routerLink]="['/tab2']"><a>
<router-outlet></router-outlet> // the content of your tab components will be displayed below
<app-footer>
This way in my opinion make your app easier to read (declarative routing) instead of having an external service and component for managing the states of your tabs.
I try to reproduce your structure with the usage of the ngComponentOutlet directive. Here is the tab-container:
#Component({
selector: 'tab',
template: ''
})
export class TabComponent {
#Input() title: string;
#Input() contentRef: any;
active = false;
}
This is a very simple component which knows its own tab name, an active state and the body component reference which should be loaded when somebody selects the tab.
Then we create several body components which will be loaded dynamically:
#Component({
selector: 'tab-content',
template: `<p>Hey</p>`
})
export class TabContentComponent {}
#Component({
selector: 'tab-content-alternative',
template: `<p>Hey, this is an alternative content</p>`
})
export class TabContentAlternativeComponent {}
Here is the tabs-container component with tabs rendering and an empty placeholder for dynamic body components:
#Component({
selector: 'tab-container',
template: `
<div class="tab-header">
<div class="tab" *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active">{{tab.title}}</div>
</div>
<div class="tab-content">
<ng-container *ngComponentOutlet="content | async"></ng-container>
</div>
`,
})
export class TabContainerComponent implements AfterContentInit {
#ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
private contentSbj = new BehaviorSubject<BasicContent>(null);
content = this.contentSbj.asObservable();
ngAfterContentInit() {
const activeTabs = this.tabs.filter((tab) => tab.active);
if (activeTabs.length === 0) {
this.selectTab(this.tabs.first);
}
}
selectTab(tab: TabComponent) {
this.tabs.toArray().forEach(tab => tab.active = false);
tab.active = true;
this.contentSbj.next(tab.contentRef);
}
}
And this is how it can be used in some parent component:
import {TabContentComponent} from './tab/tab-content.component'
import {TabContentAlternativeComponent} from './tab/tab-content-alternative.component'
...
#Component({
selector: 'my-app',
template: `
<tab-container>
<tab title="Tab 1" [contentRef]="normalContent"></tab>
<tab title="Tab 2" [contentRef]="alternativeContent"></tab>
</tab-container>
`,
})
export class App {
normalContent = TabContentComponent;
alternativeContent = TabContentAlternativeComponent;
}
Here is working Plunkr