Is there a way to toggle a checked checkbox list in Angular2?
I have a button that when pressed and the full list is in view, it will show only the checked items in the list. When the button is pressed again, it will show the entire list.
Plunkr: http://plnkr.co/edit/jZz4XoHjYJ40bjt2eOU5?p=preview
//our root app component
import {Component, NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
</div>
<li *ngFor="let col of data" class="form-group">
<input type="checkbox" name="col" value="{{col.value}}" [(ngModel)]="col.value" (change)="addColumns(col)" />{{col.name}}
</li>
`,
})
export class App {
name:string;
data:any[]=[{"id":"13","name":"AAA"},{"id":"15","name":"BBB"},{"id":"20","name":"CCC"}]
constructor() {
this.name = 'Angular2'
}
get selectedcheckboxes() {
return this.data
.filter(opt => opt.value)
}
addColumns(col){
this.selectedcheckboxes;
console.log(this.selectedcheckboxes)
}
}
#NgModule({
imports: [ BrowserModule,FormsModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
HTML:
<body>
<my-app>
loading...
</my-app>
<button class="check">Collapse/Expand</button>
</body>
In Angular1, it looks like this: http://jsfiddle.net/jzhang172/of4yy8k9/ I'm looking to do the same thing in Angular2, but can't understand the syntax.
You can put the main array in other variable and then just change the data variable according your clicked button (to expand or to collapse), you may need one variable to define if it's full list or the selected list
something like:
isFullList: boolean;
mainData: Array<any> = [your main data here];
data: Array<any> = [data to use in list]; //should initied by mandata
toggle() {
//this.isFullList: boolean
if (!this.isFullList) {
this.data = [...this.mainData];
} else {
this.data = [...this.selectedcheckboxes];
}
console.log(this.data)
this.isFullList = ! this.isFullList
}
plunker: http://plnkr.co/edit/V1iiX87gYVMUtIkmpMfT?p=preview
You can implement an filter at your component, and invoke the filter at your template.
In the filter, just add a flag to control to filter or show original list, and toggle the flag by click the button.
Invoke filter at template
*ngFor="let col of getData()"
Filter data in component
getData() {
return this.filter ? this.data.filter(item => item.value === true) : this.data;
}
Plunker Demo
Related
thank You for reading. I'm a Angular beginner and have an first problem I can't solve. I read many, many posts without success, sorry.
My seach form is working fine if I get the search phrase in one component and process it in another component. The *ngFor loop gives back the right result-array in dom and console: searchCustomer(phrase).
The exactly same search function works not, if I include a header component with another search form - although I get the right results via console(!). I included the header via app-header in component.html.
Why contains the array "results" the right items but the dom isn't showing it? This is my code:
search.component.html:
<form #f = "ngForm" action="verification" method="get">
<input [(ngModel)] = "phrase" type="text" name="phrase">
<button type="button" (click)="searchCustomer()">Search</button>
</form>
search.component.ts:
searchCustomer() {
this.dataService.setPhrase(this.phrase); // store phrase
this.router.navigate(['/results']); // navigate
}
result.component.html:
<app-header></app-header>
<div class="col-md-6" *ngFor="let vs of results">
...
</div>
result.component.ts:
import { ChangeDetectorRef, Component, OnInit } from '#angular/core';
// add ApiService
import { ApiService } from '../~services/api.service'; // ApiService
// add customers Model
import { Customer } from '../~interfaces/customers';
// DataService
import { DataService } from "../~services/data.service";
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css'],
})
export class ResultComponent implements OnInit {
// add array:
results: Customer[];
// add variable
phrase: string;
constructor(
private apiService: ApiService,
private dataService: DataService,
private cdr: ChangeDetectorRef
) {
}
ngOnInit() {
}
searchCustomer(phrase) {
// search
this.apiService.searchCustomer(phrase).subscribe((result:Customer[]) => {
this.results = result;
console.log(this.results);
this.cdr.detectChanges();
}
)
}
}
header.component.html:
<form #f = "ngForm">
<input type="text" [(ngModel)] = "phrase" type="text" value="{{phrase}}">
<button type="button" (click)="searchCustomer()">Search</button>
</form>
header.component.html:
searchCustomer(){
this.dataService.setPhrase(this.phrase);
this.resultComponent.searchCustomer(this.phrase);
}
If I start the search from Header, I can see the correct array "results" in Console, but not in dom. There is no refreshing.
Thank you for eye-opening,
Matthias
This was the solution in result.component.ts
responsePage$: Observable<ResponsePage>;
ngOnInit() {
this.apiService.searchCustomer(phrase).subscribe((result:Customer[]) => {
this.results = result;
this.searchCustomer(phrase);
});}
searchCustomer(phrase) {
this.responsePage$=this.apiService.customers(phrase);
}
This question already has answers here:
How to Update a Component without refreshing full page - Angular
(7 answers)
Closed 3 years ago.
I'm not sure whether this is possible or not in angular but I wanted to hide a global component when a specific route is opened.
Say for example I have the following:
app.component.html
<app-header></app-header>
<app-banner></app-banner> <!-- Global Component I want to hide -->
<div class="body-container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
app-routing.module.ts
import {NgModule} from '#angular/core';
import {Route, RouterModule} from '#angular/router';
import { StudentListComponent } from './Components/StudentList/StudentList.component';
import { SchoolMottoComponent } from './Components/SchoolMotto/SchoolMotto.component';
const routes: Routes = [
{path: 'StudentList', component: StudentListComponent },
{path: 'SchoolMotto', component: SchoolMottoComponent }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routingComponents = [StudentListComponent, SchoolMottoComponent]
With this, its a given that when I want to view the StudentList Component, then the url by default becomes localhost4200:/StudentList and the same with SchoolMotto it becomes localhost4200:/SchoolMotto.
Within the StudentListComponent, is an ag-grid that displays list of students, and when you click one of those students the url becomes something like this: localhost4200:/StudentList/view/cf077d79-a62d-46e6-bd94-14e733a5939d and its another sub-component of SchoolList that displays the details of that particular student.
I wanted to hide the Global banner component when the url has something like that: localhost4200:/StudentList/view/cf077d79-a62d-46e6-bd94-14e733a5939d. Only when the user views the specific details of a student.
Something like this:
app.component.html
<app-header></app-header>
**<app-banner *ngIf="router != '/StudentList/view/'"></app-banner> <!-- Global Component I want to hide -->**
<div class="body-container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
Is this doable or not? If it is, how?
You could use event emitter or subject to emit an event when you're in StudentList/view and use ngIf to hide the banner.
In your StudentList component.ts :
export class StudentList {
bannerSubject: Subject<any> = new Subject<any>();
ngOnInit() {
bannerSubject.next(true);
}
}
subscribe to this in your parent component and you can easily hide the banner.
You can acheieve that with the help of component interation using a service
You will use the help of Rxjs Observables here
You will emit an event when you reach the student view component, then recieve that event in app component then change the view condition
New Service:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable()
export class RouteService {
private routeChangedSource = new Subject<string>();
// Observable string streams
routeChanged$ = this.routeChangedSource.asObservable();
// Service message commands
changeRoute(mission: string) {
this.routeChangedSource.next(mission);
}
}
Student View Component.
import { Component } from '#angular/core';
import { routeService } from './mission.service';
#Component({
})
export class MissionControlComponent implements ngOnInit{
constructor(private routeService: routeService) {}
ngOnInit() {
this.routeService.changeRoute(mission);
}
}
App Component:
import { Component, Input, OnDestroy } from '#angular/core';
import { RouteService } from './route.service';
import { Subscription } from 'rxjs';
export class AppComponent implements OnDestroy {
studentView = false;
constructor(private routeService: RouteService) {
this.subscription = routeService.routeChanged$.subscribe(
value => {
this.studentView = true;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Now, your App Component can be:
<app-header></app-header>
<app-banner *ngIf="!studentView"></app-banner>
<div class="body-container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
<app-header></app-header>
<app-banner *ngIf="myService.hideGlobalComp"></app-banner> <!-- Global Component I want to hide -->
<div class="body-container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
in the ts file:
onCellClicked($event) { // place into your method there you want.
this.route.parent.url.subscribe(urlPath => {
this.url = urlPath[urlPath.length - 1].path;
});
if(this.url === 'StudentList/view') {
this.myService.hideGlobalComp = true;
}
}
}
In you ts file do like this.
add new variable router: string;
add in construction add this
constructor(private _router: Router){
this.router = _router.url;
}
Then in HTML use same code.
Let me know if this does not work.
I am implementing a feature which displays the selected items from a hierarchy structure, on the right.
slice.component.ts :
import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '#angular/core';
import * as API from '../../shared/api-routes';
import { DataService } from '../../shared/service/data.service';
import { TreeNode } from '../../shared/dto/TreeNode';
import { Subject } from 'rxjs/Subject';
import html from './slice.component.html';
import css from './slice.component.css';
#Component({
selector: 'slice-component',
template: html,
providers: [DataService],
styles: [css],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SliceComponent {
selections: TreeNode<string>[] = [];
newList: TreeNode<string>[];
constructor(dataService:DataService, cd:ChangeDetectorRef) {
super(dataService, cd);
}
public onSliceChange(event:TreeNode<string>):void {
if(event.selected) {
this.selections.push(event);
}
else {
var index = this.selections.indexOf(event);
if(index > -1) {
this.selections.splice(index, 1);
}
}
this.newList = this.selections.slice();
}
}
slice.component.html :
<p>Slices</p>
<mat-input-container>
<input #searchInput matInput placeholder="Search for Slices">
</mat-input-container>
<div class="flex-container">
<div class="SliceCheck" *ngIf="isDataLoaded">
<fortune-select
(sliceSelected)="onSliceChange($event)">
</fortune-select>
</div>
<div class="sendToRight">
<rightside-component
[sliceTreeNode]="newList">
</rightside-component>
</div>
</div>
rightside.component.ts :
import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '#angular/core';
import { TreeNode } from '../../shared/dto/TreeNode';
import html from './rightside.component.html';
import css from './rightside.component.css';
#Component({
selector: 'rightside-component',
template: html,
providers: [DataService],
styles: [css],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RightSideComponent {
#Input() sliceTreeNode: TreeNode<string>[];
constructor(private cd: ChangeDetectorRef) {}
getSlices() : TreeNode<string>[] {
if (typeof(this.sliceTreeNode) == "undefined" || (this.sliceTreeNode) === null) {
return [];
}
return this.sliceTreeNode;
}
deselect(item: TreeNode<string>): void {
if((item.children) !== null) {
item.children.forEach(element => {
this.deselect(element);
});
}
var index = this.sliceTreeNode.indexOf(item);
if(index > -1) {
this.sliceTreeNode.splice(index, 1);
}
item.selected = false;
}
}
rightside.component.html :
<ul class="selection-list" >
<li *ngFor="let item of getSlices()">
<button class="btn" (click)="deselect(item)" *ngIf="item.selected">
<i class="fa fa-close"> {{ item.displayName }} </i>
</button>
</li>
</ul>
In my implementation, everything works as expected until the following happens :
You delete an item from the rightside list, it gets deselected correctly from the hierarchy. But when you select it again from the hierarchy, it shows up twice in the side-view list now.
Somehow the list instance in the right-side component does not get updated when a node is selected again which has been previously deselected.
Any inputs on how to fix this? It is something similar to this plunkr I found online : http://next.plnkr.co/edit/1Fr83XHkY0bWd9IzOwuT?p=preview&utm_source=legacy&utm_medium=worker&utm_campaign=next&preview
The most likely issue is that you're using ChangeDetectionStrategy.OnPush and performing mutations within onSliceChange of the SliceComponent without an explicit call to cd.markForCheck().
Either remove changeDetection: ChangeDetectionStrategy.OnPush from SliceComponent or add cd.markForCheck() to the end of onSliceChange
When you set the change detection to OnPush, Angular only guarantees that the component will be updated when the references passed to it's Inputs are changed. onSliceChange doesn't replace any Input, so the slice component won't be updated.
You should also consider replacing deselect in RightSideComponent with an Output and handling the changes in SliceComponent. The convention of one-way data flow is to only modify common state in the common parent. This prevents conflicts when two components both want to modify some shared state.
hello i am new in angular 2
i can make formGroup in add in ng-select controll and predefine value added.
that is perfectly.
but when button click then new value push in ng-select but ng-select not updating .
here my plunker
https://plnkr.co/edit/Hwfk1T2stkiRcLTxuFmz
//our root app component
import {Component, OnInit, NgModule, ViewChild} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {FormControl, FormGroup, ReactiveFormsModule} from '#angular/forms';
import {SelectModule} from 'ng-select';
#Component({
selector: 'my-app',
template: `
<h1>ng-select demo app</h1>
<form style="padding:18px;max-width:800px;"
[formGroup]="form">
<div style="margin:5px 0;font-weight:600;">Single select example</div>
<ng-select
[options]="options0"
[multiple]="false"
placeholder="Select one"
formControlName="selectSingle"
>
</ng-select>
<button (click)="pushValue()">Click</button>
<div>Events:</div>
<pre #preSingle>{{logSingleString}}</pre>
</form>`
})
export class App implements OnInit {
form: FormGroup;
multiple0: boolean = false;
options0: any[] = [];
selection: Array<string>;
#ViewChild('preSingle') preSingle;
logSingleString: string = '';
constructor() {
this.options0.push({"label":'test',"value":'Test'});
console.log("Object:::"+JSON.stringify(this.options0));
}
ngOnInit() {
this.form = new FormGroup({});
this.form.addControl('selectSingle', new FormControl(''));
console.log("Object:::"+JSON.stringify(this.options0));
}
pushValue()
{
console.log("pushValue call.");
this.options0.push({"label":"test","value":"Test"});
console.log("Object:::"+JSON.stringify(this.options0));
}
}
#NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
SelectModule
],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
where is wrong ???
you can use Array.slice() to update to array instance in order to let angular detect the change of array.
this.options0 = this.options0.slice();
Looking at ng-select source code i noticed
ngOnChanges(changes: any) {
if (changes.hasOwnProperty('options')) {
this.updateOptionsList(changes['options'].isFirstChange());
}
so in order to update options list you should fire ngOnChanges. It can be done by creating new reference to options0
this.options0 = this.options0.concat({"label":"test","value":"Test"});
or
this.options0 = [...this.options0, {"label":"test","value":"Test"}];
Modified Plunker
Change Detection
Ng-select component implements OnPush change detection which means the dirty checking checks for immutable data types. That means if you do object mutations like:
this.items.push({id: 1, name: 'New item'})
Component will not detect a change. Instead you need to do:
this.items = [...this.items, {id: 1, name: 'New item'}];
This will cause the component to detect the change and update. Some might have concerns that this is a pricey operation, however, it is much more performant than running ngDoCheck and constantly diffing the array.
I got some issues on autocomplete on Angular2 Material Design which these are the things happened:
*When I type a character that is associated with the one I search it won't display on the autocomplete the specific character that I entered on the search textbox as shown in the picture below:
When before typing on the search box.
When after typing on the search box
The second one is that when I select a specific list on the list of users it seems that it will display the [object Object] thingy and I don't know why it happens. See the picture below:
When before selecting a employee on the list
When after selecting a employee on the list
Here is my code below see if there's something that I missed or what.
Angular Code: new-useraccount-components.ts
import { Component } from '#angular/core';
import { WebServiceComponents } from '../WebService/web.service';
import { FormControl } from '#angular/forms';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/map';
#Component({
selector: 'new-user-account',
template: `
<md-card class="card-margin">
<md-card-content>
<md-input-container>
<input mdInput placeholder="Select Employee" [mdAutocomplete]="auto" [formControl]="UserAccountCtrl" /><br />
</md-input-container>
<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let userAccount of filterUserAccount | async" [value]="userAccount">
{{userAccount.username}}
</md-option>
</md-autocomplete>
<md-input-container>
<input mdInput placeholder="Username" /><br />
</md-input-container>
</md-card-content>
</md-card>
`
})
export class NewUserAccountComponent{
UserAccountCtrl: FormControl;
filterUserAccount: any;
async ngOnInit(){
var response = await this.webService.getUserAccounts();
this.userAccounts = response.json();
}
userAccounts = [];
constructor(private webService : WebServiceComponents){
this.UserAccountCtrl = new FormControl();
this.filterUserAccount = this.UserAccountCtrl.valueChanges
.startWith(null)
.map(name => this.filteredUserAccount(name));
}
filteredUserAccount(val: string) {
return val ? this.userAccounts.filter(s => new RegExp(`^${val}`, 'gi').test(s))
: this.userAccounts;
}
}
AppModule: app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { MaterialModule } from '#angular/material';
import { AppComponent } from './app.component';
import { WebServiceComponents } from './WebService/web.service';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { UserAccountComponent } from './UserAccount/useraccount-components';
import { NewUserAccountComponent } from './UserAccount/new-useraccount-component';
#NgModule({
imports: [ BrowserModule, MaterialModule, ReactiveFormsModule, FormsModule ],
declarations: [ AppComponent, UserAccountComponent, NewUserAccountComponent ],
bootstrap: [ AppComponent ],
providers: [ WebServiceComponents ]
})
export class AppModule { }
Sorry I'm just a newbie trying to play with this new framework. Hopefully someone will guide me on how to fix this issue. Thank you and Have a wonderful day ahead!
The docs are quite clear if you read them closely enough:
https://material.angular.io/components/autocomplete/overview
You'll be especially interested in what it has to say under the title "Setting separate control and display values". It is not displaying your search result because you want to display something different than the [value] you've specified.
The other problem you are having is that when selecting an item in the list the actual value of that item is inserted into the search box. Since this is a userAccount and not the username, [object Object] is displayed.
Simply changing to [value]="userAccount.username" could solve both your problems. If this does not give you the desired effect, you'll have to use the [displayWith] attribute as explained in the docs.