I am using material design and have a dialogService set up for dynamically loading MdDialog. I'm trying to make a search dialog with search filters that when submitted it brings you to a search-results component route. And I can't figure out how to output the search data to the search-results component.
./dialog-service.ts
#Injectable()
export class DialogService {
private dynamicModalComponent: DialogComponent;
public init(dynModal: DialogComponent) {
this.dynamicModalComponent = dynModal;
}
public show(component: Type<any>, configuration?: MdDialogConfig) {
this.dynamicModalComponent.showModal(component, configuration);
}
public hide() {
this.dynamicModalComponent.hideModal();
}
}
./modules/search.component.html
<div class="search-component">
<h2>Search</h2>
<md-input-container class="full-width search">
<input mdInput placeholder="search" color="primary" />
</md-input-container>
<div class="radio-groups">
<md-radio-group class="radio-buttons" [(ngModel)]="searchFilterValue">
<md-radio-button class="r-button" [value]="sf.value" *ngFor="let sf
of searchFilter">
{{sf.name}}
</md-radio-button>
</md-radio-group>
</div>
<md-dialog-actions class="actions">
<button md-button (click)="hide()">Cancel</button>
<button md-raised-button (click)="search()"
color="primary">Search</button>
</md-dialog-actions>
</div>
./modules/search.component.ts
import {Component, OnInit} from "#angular/core";
import {DialogService} from "../dialog/dialog.service";
import {Router} from "#angular/router";
#Component({
selector: 'search',
templateUrl: './search.component.html',
styleUrls:['./search.component.scss']
})
export class SearchComponent implements OnInit {
searchFilterValue;
searchFilter = [
{
name: 'Groups',
value: 'groups',
},
{
name: 'People',
value: 'users',
},
{
name: 'Events',
value: 'events',
},
{
name: 'Posts',
value: 'posts',
}
];
constructor(private _dialogService: DialogService,
private router: Router){
this.searchFilterValue = 'groups';
}
ngOnInit(){}
hide() {
this._dialogService.hide();
}
search() {
this.hide();
this.router.navigate(['/search']);
}
}
You have several options:
1) You could use optional or query routing parameters. Then as part of the router.navigate, you'd also pass along the parameters. This is a great option if you need to pass data from this component directly to another component.
2) Another option is to build a service. The service holds onto the search filter values. The search component sets the values into the service and the component then reads the values from the service.
Do one of these options sound like they could work for you?
You can define an output event for hide/close event and pass result as event argument.Other components can subscribe such event to handle result.
Related
Transfer single item from a list item to cart list.
I am developing an Angular web app and want that when I click a button the single item of an array gets transferred from one service to another service and is also transferred on another component. I have successfully implemented it with a transfer of whole array but I am facing problem with a single item.Please help.
What I want is that when I click on Add to cart button the list item which is clicked only gets transferred and not the array of list items.
buyGame.html file
<div class="col-xs-6">
<a class="list-group-item clearfix" style="background-color:rgb(3, 0, 48)" *ngFor="let buying of buy">
<div class="pull-left" style="max-width:330px">
<h5 style="color:white">{{buying.names}}</h5>
<p style="color:white">{{buying.desc}}</p>
<button class="btn btn-danger ; pull-left" (click)= "onAddToCart()">Add To Cart</button>
</div>
<div>
<span class="pull-right">
<img [src]="buying.getImg" alt="image not loaded" class="img-responsive" style="max-height:100px">
</span>
</div>
</a>
</div>
buygame.service.ts file :
import { gameBuy } from "./buygame.model";
import { Injectable,EventEmitter } from "#angular/core";
import { cartService } from "./cart.service";
#Injectable()
export class gameService{
private gameServ: gameBuy[] = [
new gameBuy('batman','Batmobile and enhancements to signature features',"https://www.geek.com/wp-content/uploads/2016/02/batmans-625x352.jpg"),
new gameBuy('GTA 5',
"PlayStation 3 or Xbox 360 will be able to transfer their current Grand Theft Auto Online characters and progression to their choice of PlayStation 4 Xbox One or PC",
"http://onlysp.com/wp-content/uploads/2015/01/maxresdefault.jpg")
];
constructor(private cartSer: cartService){}
getBuyingList(){
return this.gameServ.slice();
}
addItemToCart(game:gameBuy[]){
this.cartSer.addItem(game);
}
}
buyGame.component.ts:
import { Component, OnInit,Input } from '#angular/core';
import { gameBuy } from '../shared/buygame.model';
import { gameService } from '../shared/buygame.service';
#Component({
selector: 'app-buy-game',
templateUrl: './buy-game.component.html',
styleUrls: ['./buy-game.component.css'],
})
export class BuyGameComponent implements OnInit {
#Input() buy:gameBuy[];
constructor(private service: gameService) { }
ngOnInit() {
this.buy = this.service.getBuyingList();
}
onAddToCart(){
this.service.addItemToCart(this.buy);
}
}
cart.component.ts:
import { Component, OnInit} from '#angular/core';
import { cartModel } from '../shared/cart.model';
import { cartService } from '../shared/cart.service';
import { gameBuy } from '../shared/buygame.model';
#Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css'],
})
export class CartComponent implements OnInit {
cart:gameBuy[];
constructor(private service: cartService) { }
ngOnInit() {
this.cart = this.service.getCartItem();
}
}
cart.service.ts:
import { cartModel } from "./cart.model";
import { EventEmitter } from "#angular/core";
import { gameBuy } from "./buygame.model";
export class cartService{
cartChanged = new EventEmitter<gameBuy[]>();
private cart: gameBuy[] = [
new gameBuy('Batman','Batman is a cool game','https://images-na.ssl-images-amazon.com/images/I/91lu5KHSm3L._SY445_.jpg'),
new gameBuy('Gta 5','online game of GTA','https://www.rockstargames.com/V/img/global/order/mobile-cover.jpg')
];
getCartItem(){
return this.cart.slice();
}
addItem(cart:gameBuy[]){
this.cart.push(...cart);
this.cartChanged.emit(this.cart.slice());
}
}
cart.model.ts:
export class cartModel{
constructor(public cartName: string,public cartDesc: string,public cartImage:string){}
}
buygame.model.ts:
export class gameBuy{
constructor(public names:string, public desc:string, public getImg:string){}
}
You need to specify exact item you want to be added to the cart in the temlate:
(click)= "onAddToCart(buying)"
And then pass it right to your service as onAddToCart method parameter:
onAddToCart(buying: gameBuy){
this.service.addItemToCart(buying);
}
Also, your buygame service method should accept a single item, not a list:
addItemToCart(game: gameBuy){
this.cartSer.addItem(game);
}
Atl last, cart service should be updated too (just to push a single item)
addItem(cart:gameBuy){
this.cart.push(cart);
this.cartChanged.emit([...this.cart]); // slice() is ok too if you need a copy
}
Try providing the index in your call (click)= "onAddToCart(index)" and get it from your array.
OR provide the single object in (click)= "onAddToCart(buying)"
then receive it on TS
I am trying to abstract out a tabular-data display to make it a child component that can be loaded into various parent components. I'm doing this to make the overall app "dryer". Before I was using an observable to subscribe to a service and make API calls and then printing directly to each component view (each of which had the tabular layout). Now I want to make the tabular data area a child component, and just bind the results of the observable for each of the parent components. For whatever reason, this is not working as expected.
Here is what I have in the parent component view:
<div class="page-view">
<div class="page-view-left">
<admin-left-panel></admin-left-panel>
</div>
<div class="page-view-right">
<div class="page-content">
<admin-tabs></admin-tabs>
<table-display [records]="records"></table-display>
</div>
</div>
</div>
And the component file looks like this:
import { API } from './../../../data/api.service';
import { AccountService } from './../../../data/account.service';
import { Component, OnInit, Input } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { TableDisplayComponent } from './../table-display/table-display.component';
#Component({
selector: 'account-comp',
templateUrl: 'app/views/account/account.component.html',
styleUrls: ['app/styles/app.styles.css']
})
export class AccountComponent extends TabPage implements OnInit {
private section: string;
records = [];
errorMsg: string;
constructor(private accountService: AccountService,
router: Router,
route: ActivatedRoute) {
}
ngOnInit() {
this.accountService.getAccount()
.subscribe(resRecordsData => this.records = resRecordsData,
responseRecordsError => this.errorMsg = responseRecordsError);
}
}
Then, in the child component (the one that contains the table-display view), I am including an #Input() for "records" - which is what the result of my observable is assigned to in the parent component. So in the child (table-display) component, I have this:
import { AccountService } from './../../../data/account.service';
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'table-display',
templateUrl: './table-display.component.html',
styleUrls: ['./table-display.component.less']
})
export class TableDisplayComponent {
#Input() records;
constructor() {
}
}
Lastly, here's some of the relevant code from my table-display view:
<tr *ngFor="let record of records; let i = index;">
<td>{{record.name.first}} {{record.name.last}}</td>
<td>{{record.startDate | date:"MM/dd/yy"}}</td>
<td><a class="bluelink" [routerLink]="['/client', record._id ]">{{record.name.first}} {{record.name.last}}</a></td>
When I use it with this configuration, I get "undefined" errors for the "records" properties I'm pulling in via the API/database. I wasn't getting these errors when I had both the table display and the service call within the same component. So all I've done here is abstract out the table-display so I can use it nested within several parent components, rather than having that same table-display show up in full in every parent component that needs it.
What am I missing here? What looks wrong in this configuration?
You need to protect against record being null until it comes in to your child component (and therefore it's view).
Use Elvis operators to protect your template:
<tr *ngFor="let record of records; let i = index;">
<td>{{record?.name?.first}} {{record?.name?.last}}</td>
<td>{{record?.startDate | date:"MM/dd/yy"}}</td>
<td><a class="bluelink" [routerLink]="['/client', record?._id ]"> {{record?.name?.first}} {{record?.name?.last}}</a></td>
You can also assign your input to an empty array to help with this issue:
#Input() records = [];
In my Angular2 app I am looping through a list of users, and for each user in the list, I am populating an icon to the view to represent that person. With that working, I'd like to now use material2's tooltip (mdTooltip) to show the name when someone scrolls over the icon. I can get the tooltip working when I connect it to a singular property in my component via string interpolation, like for "name: 'John Smith'" I can just use "{{name}}" in my component HTML. But when I try and pull the name out of an array from that same component, it doesn't work.
This is what my component looks like:
import { User } from './../../views/user/user';
import { Component, OnInit, Input } from '#angular/core';
import { AuthenticationService } from './../../data/authentication.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-room',
templateUrl: './room.component.html',
styleUrls: ['./room.component.less']
})
export class RoomComponent implements OnInit {
otherImg = 'app/img/photo-ph.png';
model: any;
loading = false;
name = 'John Smith';
others = [
{ id: 1, name: 'John Smith', avatar: 'app/img/photo-ph.png' },
{ id: 2, name: 'Javier Sanchez', avatar: 'app/img/photo-ph.png' }
];
user;
token;
nickname;
constructor(private authenticationService: AuthenticationService,
private router: Router) { }
isLoggedIn() {
this.loading = true;
if (this.authenticationService.isAuthenticated()) {
return true;
}
}
ngOnInit() {
}
}
And here's the version of my component HTML that works:
<div *ngIf="isLoggedIn()" class="others">
<span *ngFor="let other of others"><i [ngClass]="'material-icons'" [routerLink]="['/chat']" mdTooltip="{{name}}" tooltip-position="below">person</i></span>
<a [routerLink]="['/login']">Logout</a>
</div>
But when I try string interpolation to pull a value out of an array and use it in the tooltip, it doesn't work:
<div *ngIf="isLoggedIn()" class="others">
<span *ngFor="let other of others"><i [ngClass]="'material-icons'" [routerLink]="['/chat']" mdTooltip="{{others.name}}" tooltip-position="below">person</i></span>
<a [routerLink]="['/login']">Logout</a>
</div>
In your case others is an array, so it doesn't have a "name" property. By you already iterate over it, and put each value into "other".
So this will work:
mdTooltip="{{other.name}}"
I think you are using the array and not the instance var
{{others.name}}
should be
{{other.name}}
Hoping for some help on a more complex example that expands on the examples in angular's Tour of Heroes
Rather than submitting a single string each time, how would you submit multiple values like in the following example e.g.:
export class LittleTourComponent {
heroes = [ {
'name':'Hulk',
'power':'strength'
},{
'name':'Bulk',
'power':'appetite'
}];
I presume a new 'entry' made up of the submitted values should be pushed to the heroes array something like this:
addHero(newHero) {
if (newHero) {
var entry = {
'name': newHero.name,
'power': newHero.power
};
this.heroes.push(entry);
}
}
But what would be required in the template? Would you still use keyup.enter in this case?:
template:
<label>name</label
// how should the inputs be filled out in this scenario?
<input >
<label>power</label>
<input >
<button (click)=addHero(newHero)>Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
example also on plnkr
Any help appreciated. thanks!
Try and do this in your ts file:
import {Component} from '#angular/core';
class Hero {
name: string;
power: string;
}
export class LittleTourComponent {
newHero: Hero;
constructor() {
this.newHero = new Hero();
}
heroes = [{
'name': 'Hulk',
'power': 'strength'
}, {
'name': 'Bulk',
'power': 'appetite'
}];
addHero() {
if (this.newHero) {
var entry = {
'name': this.newHero.name,
'power': this.newHero.power
};
this.heroes.push(entry);
}
}
}
...and this in your html
<label>name</label>
<input [(ngModel)]="newHero.name">
<label >power</label>
<input [(ngModel)]="newHero.power">
<button (click)=addHero()>Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
your click listener is calling what it thinks is a reference to an element in the DOM which u havent defined nor would take paramaters. Trying putting quotes around that callback
<label>name</label
// how should the inputs be filled out in this scenario?
<input >
<label>power</label>
<input >
<button (click)="addHero(newHero)">Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
after further review, i notice ur referencing newHero in the little-tour component which does not exist in that components scope. Also, uve bound correctly to your inputs but i dont believe .value is the correct property to return the input... try
[(ngModel)]="input1"
in your class declaration ad
input1: String;
and then using that variable.
I didnt notice until right now that you arent importing your directive
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html'
})
export class AppComponent { }
since u are calling
<little-tour></little-tour>
in your app.component.html then this should be your app component
import { Component } from '#angular/core';
import {LittleTourComponent} from 'path-to-little-tour'
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [LittleTourComponent]
})
export class AppComponent { }
I have a route called home and it has three child routes, documents, mail and trash. In the home route component it has a variable called 'user'. I know there are a few ways of passing info between parent and child components highlighted here, but how am I suppose to pass info between parent/child routes.
{ path: 'home', component: HomeComponent, children: [
{ path: 'documents', component: DocumentsComponent },
{ path: 'mail', component: MailComponent },
{ path: 'trash', component: TrashComponent },
]
},
Service
import { Injectable } from '#angular/core';
#Injectable()
export class HomeService {
// Mock user, for testing
myUser = {name:"John", loggedIn:true};
// Is Super Admin
isLogged():boolean {
if(this.myUser.role == true){
return true ;
}
return false ;
}
}
Component
constructor(public router: Router, public http: Http, private homeService: HomeService) {
}
isLogged(){
return this.homeService.isLogged();
}
Template
<div class="side-nav fixed" >
<li style="list-style: none">
<img alt="avatar" class="circle valign profile-image" height="64" src=
"../images/avatar.jpg" width="64">
<div class="right profile-name">
<!-- Value not changing even with service -->
{{myUser.role}}
</div>
</li>
You may use a common service to pass data like explained in the Angular Documentation
Basically you may create a Service which will have a user object, which can be updated once your parent route gets loaded or with some action on parent component.
UserService
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class UserService {
// Observable user
user = new Subject<string>();
}
And then when the child route component gets loaded you may retrieve the value from the Service.
HomeComponent
#Component({
...
})
export class HomeComponent{
...
constructor(private userService:UserService ){}
someMethod = () =>{
this.userService.user.next(<pass user object>);
}
}
MailComponent
#Component({
...
})
export class HomeComponent{
...
constructor(private userService:UserService ){
this.userService.user.subscribe(userChanged);
}
userChanged = (user) => {
// Do stuff with user
}
}
Service object will be same instance in child if you add the provider in the parent.
Check out :- https://angular.io/docs/ts/latest/guide/router.html#!#link-parameters-array
You can pass data while changing routes on click as :-
<a [routerLink]="['/crisis-center', { foo: myVar }]">Crisis Center</a>