How to pass value from template HTML to component to then be used in service - angular5

I want to fetch id from component.html into component.ts to pass it to a service.
.ts file is;
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { HttpErrorResponse } from '#angular/common/http/src/response';
import { SendUsingApiService } from '../send-using-api.service';
import { Router, ActivatedRoute } from '#angular/router';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { setDefaultService } from 'selenium-webdriver/chrome';
#Component({
selector: 'app-org-info',
templateUrl: './org-info.component.html',
styleUrls: ['./org-info.component.css'],
providers: [SendUsingApiService]
})
export class OrgInfoComponent implements OnInit {
orgData: string[] = [];
Id = 1;
editRecord:FormGroup;
constructor(private httpService: HttpClient, private _serv: SendUsingApiService,
private fb: FormBuilder, private _ar:ActivatedRoute, private _r:Router) {
this.editRecord = this.fb.group({
Id:['1', []],
OrganisationName:['', []],
ContactPerson:['', []],
ContactPersonHPNo:['', []],
ContactPersonEmailId:['', []]
});
}
ngOnInit() {
console.log(this._ar.snapshot.params.Id, "+ve");
this._ar.params.subscribe(() => {
this._serv.getUsers(this._ar.snapshot.params.Id).subscribe((res)=>{
console.log(res);
this.setUser(res);
});
});
}
I am getting the value for console.log(this._ar.snapshot.params.Id); as undefined "+ve".
I want to get the Id value in console.
As per requests I am adding html part, though little adjusted;
<td style="text-align: center;">
<a class="btn btn-basic" [routerLink]="['/org-info',data['Id']]" role="button" (click)="getOrgData(data.Id)">View</a>
</td>

I defined a property instead of Id = 1; (above)
paramId = '';
then, within ngOnInit;
ngOnInit() {
this.paramId = this._ar.snapshot.params.Id;
console.log(paramId, "+ve");
}
Doing this, I got the Id value instead of undefined.

Related

How could I use rxjs debounceTime and distinctUntilChanged with angular input events (e.g., click or input)

How could I use rxjs debounceTime and distinctUntilChanged with angular input events (input) or (click)
I can't use fromEvent because I also need to pass parameter (Example below)
<li *ngFor="let item of items>
<input [(ngModel)]="item.name" (input)="inputChanged(item.id)">
</li>
So I went with Subject (Example below)
<input type="text" placeholder="Type something..." (input)="inputFn($event, 'myParam')" #myInput>
#ViewChild("myInput", { static: true }) myInput;
private inputEvent = new Subject<any>();
ngOnInit() {
this.inputEvent
.pipe(
// map((obj: any) => obj.evt.target.value),
debounceTime(1000),
distinctUntilChanged()
)
.subscribe(res => {
console.log("res", res.evt.target.value);
});
}
inputFn(evt, param) {
this.inputEvent.next({evt, param});
}
In the above example, there is no use of distinctUntilChanged(). If I filter with map map((obj: any) => obj.evt.target.value) and look for value change (distinct) I am going to get only input value not parameter.
My requirement is - I want to wait until user finished entering text and also if user re-enter want to check if value is not same as previous and also want to get parameter.
You have to use in distinctUntilChanged operator the compare function
import { Component, ViewChild, OnInit } from '#angular/core';
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
#ViewChild("myInput", { static: true }) myInput;
private inputEvent = new Subject<any>();
ngOnInit() {
this.inputEvent
.pipe(
debounceTime(1000),
distinctUntilChanged((previous: any, current: any) => previous.value === current.value)
)
.subscribe(res => {
console.log("res", res.evt.target.value);
});
}
inputFn(evt, param) {
this.inputEvent.next({evt, param, value: evt.target.value});
}
}
stackblitz

TypeError: this.navigator.element.pushPage is not a function

Im using onsen-ui with angular 8 I'm getting error in the below function, I am not sure why this happens, because it is the code from the onsenUI-page.
push(event, value){
console.log(value);
this.navigator.element.pushPage(value, {animation: 'slide'});
}
My code
Sidemenu.html
<ons-page id="menu">
<ons-toolbar>
<div class="center">Menu</div>
</ons-toolbar>
<div class="background"></div>
<div class="content">
<ons-list>
<ons-list-item (click)="push($event, 'HomeComponent')" tappable>
Home
</ons-list-item>
<ons-list-item (click)="push($event, 'FormComponent')" tappable>
Form
</ons-list-item>
</ons-list>
</div>
</ons-page>
sidemenucomponent.html
import { Component, OnInit } from '#angular/core';
import { ViewChild, OnsNavigator} from 'ngx-onsenui';
import { HomeComponent } from './../home/home.component';
#Component({
selector: 'ons-page[third]',
templateUrl: './sidemenu.component.html',
styleUrls: ['./sidemenu.component.scss'],
providers:[OnsNavigator]
})
export class SidemenuComponent implements OnInit {
constructor(private navigator: OnsNavigator) { }
ngOnInit() {
}
push(event, value){
console.log(value);
this.navigator.element.pushPage(value, {animation: 'slide'});
}
}
Im getting error
ERROR TypeError: this.navigator.element.pushPage is not a function
at SidemenuComponent.push (sidemenu.component.ts:20)
at Object.eval [as handleEvent] (VM5587 SidemenuComponent.ngfactory.js:21)
at handleEvent (core.js:43993)
at callWithDebugContext (core.js:45632)
at Object.debugHandleEvent [as handleEvent] (core.js:45247)
at dispatchEvent (core.js:29804)
at core.js:42925
at HTMLElement.<anonymous> (platform-browser.js:2668)
at ZoneDelegate.invokeTask (zone-evergreen.js:391)
at Object.onInvokeTask (core.js:39680)
Below is the app component i added but still getting same error
app.component.html
<ons-navigator swipeable [page]="sidePage"></ons-navigator>
<ons-page id="apppage">
<ons-splitter #splitter>
<ons-splitter-side [page]="sidePage" side="left" width="300px" collapse swipeable>
</ons-splitter-side>
<ons-splitter-content [page]="contentPage">
</ons-splitter-content>
</ons-splitter>
</ons-page>
<router-outlet></router-outlet>
app.component.ts
import { Component } from '#angular/core';
import { HomeComponent } from './components/home/home.component';
import { SidemenuComponent } from './components/sidemenu/sidemenu.component';
import { MenuService } from './menu-service.service';
import {
Injectable,
ViewChild,
} from 'ngx-onsenui';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'my-app';
sidePage = SidemenuComponent;
contentPage = HomeComponent;
#ViewChild('splitter',{static: false}) splitter;
constructor(private menuService: MenuService) {
this.menuService.menu$.subscribe(() => this.splitter.nativeElement.side.open());
}
}
Please can anyone help.

*ngIf works OK in some parts, but not in others. Angular 2.0.x

I am trying to use *ngIf in a simple component and angular is throwing an error on *ngIf.
Removing the "*ngIf="loggedIn" and it runs. {{loggedIn}} shows true after logging in and false before.
The main app uses the same div to enable menus, which works fine.
I must be doing some simple wrong but it escapes me.
thanks
First the error:
Can't bind to 'ngIf' since it isn't a known property of 'div'. ("<div [ERROR ->]*ngIf="loggedIn">
<div class='panel panel-primary'>
<div class='panel-heading' >
"): ShowConfigComponent#0:5
Property binding ngIf not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section. ("[ERROR ->]<div *ngIf="loggedIn">
<div class='panel panel-primary'>
<div class='panel-heading' >
"): ShowConfigComponent#0:0 ; Zone: <root> ; Task: Promise.then ; Value: Error: Template parse errors:(…) Error: Template parse errors:
Can't bind to 'ngIf' since it isn't a known property of 'div'. ("<div [ERROR ->]*ngIf="loggedIn">
<div class='panel panel-primary'>
<div class='panel-heading' >
"): ShowConfigComponent#0:5
Property binding ngIf not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section. ("[ERROR ->]<div *ngIf="loggedIn">
<div class='panel panel-primary'>
<div class='panel-heading' >
"): ShowConfigComponent#0:0
at TemplateParser.parse (http://127.0.0.1:8080/node_modules/#angular/compiler/bundles/compiler.umd.js:8446:21)
at RuntimeCompiler._compileTemplate (http://127.0.0.1:8080/node_modules/#angular/compiler/bundles/compiler.umd.js:16824:53)
at eval (http://127.0.0.1:8080/node_modules/#angular/compiler/bundles/compiler.umd.js:16746:85)
at Set.forEach (native)
at compile (http://127.0.0.1:8080/node_modules/#angular/compiler/bundles/compiler.umd.js:16746:49)
at ZoneDelegate.invoke (http://127.0.0.1:8080/node_modules/zone.js/dist/zone.js:203:28)
at Zone.run (http://127.0.0.1:8080/node_modules/zone.js/dist/zone.js:96:43)
at http://127.0.0.1:8080/node_modules/zone.js/dist/zone.js:462:57
at ZoneDelegate.invokeTask (http://127.0.0.1:8080/node_modules/zone.js/dist/zone.js:236:37)
at Zone.runTask (http://127.0.0.1:8080/node_modules/zone.js/dist/zone.js:136:47)
Template:
<div *ngIf="loggedIn">
<div class='panel panel-primary'>
<div class='panel-heading' >
Shows Configuration {{loggedIn}}
</div>
</div>
Component:
import { Component, OnInit } from '#angular/core';
import { IShowConfig } from './iShowConfig';
import { AuthService } from '../../shared/AuthService';
import { BrowserModule } from '#angular/platform-browser';
#Component({
// selector: 'showconfig',
templateUrl: 'app/showMenu/showConfig/ShowConfig.component.html'
})
export class ShowConfigComponent implements OnInit {
pageTitle: string = 'Product List';
imageWidth: number = 50;
imageMargin: number = 2;
showImage: boolean = false;
listFilter: string = '';
errorMessage: string;
shows: IShowConfig[];
public isLoggedIn: boolean = true;
// constructor(private _productService: ProductService) {
constructor(public authSvc: AuthService) {
this.isLoggedIn = true;
console.log(`config loggedIn: `);
console.log(`${AuthService.isLoggedIn}`);
if (authSvc.user === undefined) {
console.log('islogin undefine');
} else {
console.log('config user: ' + authSvc.user);
}
console.log('show config count: ' + AuthService.count);
}
public get loggedIn(): boolean {
if (AuthService.isLoggedIn) {
return true;
} else {
return false;
}
}
toggleImage(): void {
this.showImage = !this.showImage;
}
ngOnInit(): void { }
onRatingClicked(message: string): void {
this.pageTitle = 'Show List: ' + message;
}
}
I found the solution! I think Nicu also guessed what it was.
I didn't import BrowserModule into my ShowConfig.module.ts ie: my #ngmodule.
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { ShowConfigComponent } from './ShowConfig.component';
import { AuthService } from '../../shared/AuthService';
import { BrowserModule } from '#angular/platform-browser';**
#NgModule({
imports: [
// The following line was missing
BrowserModule,
FormsModule
],
declarations: [
ShowConfigComponent
],
providers: [
]
})
export class ShowConfigModule { }

"attached" or DOM-render equivalent for nested view-model.ref

We have a page, "parent", which references a template via the view-model.ref called "child" in the parent.html. We change the data of this child template by clicking on items on the parent page which invokes the child function using OpenDetailsDiv. Say I use a button for this event like below:
parent.html
<child view-model.ref="clientusertasks"></child>
<input type="button" value="Click Me" click.trigger="OpenDetailsDiv" />
In this manner we can invoke a function on the "child" view-model from the parent view-model like so:
parent.js
import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';
import 'fetch';
import AuthService from 'AuthService';
import { BpoClientUserTasks } from './bpo-client-user-tasks';
#inject(HttpClient, AuthService, BpoClientUserTasks)
export class Parent {
smallDivObj = {};
freq = '';
period = '';
filterVal = '';
client = '';
constructor(http, AuthService, BpoClientUserTasks) {
http.configure(config => {
config
.withBaseUrl("WebServices.asmx/")
.withDefaults({
headers: {
'Accept': 'application/json'
}
});
});
this.http = http;
this.auth = AuthService;
this.clientusertasks = BpoClientUserTasks;
}
OpenDetailsDiv(myObject) {
this.clientusertasks.CallBPOClientUserService(this.freq, this.period, this.filterVal, myObject.TrueClient, myObject.Client);
}
}
All good so far. The "child" view-model has this function CallBPOClientUserService which looks like the following:
child.js
import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';
import 'fetch';
import AuthService from 'AuthService';
#inject(HttpClient, AuthService)
export class Child {
smallDivObj = {};
constructor(http, AuthService) {
http.configure(config => {
config
.withBaseUrl("WebServices.asmx/")
.withDefaults({
headers: {
'Accept': 'application/json'
}
});
});
this.http = http;
this.auth = AuthService;
}
attached() {
}
CallBPOClientUserService(freq, period, filterVal, client, displayClient) {
$('#TasksByClientByUserDiv').addClass("fade");
this.freq = freq;
this.period = period;
this.filterVal = filterVal;
this.client = client;
var mymethod = {
method: 'post',
body: JSON.stringify({
"session": this.auth.session,
"Client": client,
"FreqFilter": freq,
"FilterVal": filterVal
}),
headers: {
'content-type': 'application/json'
}
};
//alert(JSON.stringify(mymethod));
this.http.fetch('GetBPOTasksByClientByUserDiv', mymethod)
.then(response => response.json())
.then(info => {
this.tasksByClientByUser = JSON.parse(info.d);
//setTimeout("$('#TasksByClientByUserTable').tablesorter();", 100);
});
}
}
Notice that in the function CallBPOClientUserService we wish to call a tablesorter sort function to sort our table in the view AFTER the DOM has been rendered.
Usually I would call upon this function in the "attached" component lifecycle of the view-model. But you can see that the manner in which we are populating this view is from the view-model.ref of the "parent" page which renders the "attached" component for "child" useless for this case (it's only called once after all when the parent is loaded).
So to my question:
Is there an equivalent attached-like component that I tap into to call this tablesorter function?
I have a cheap work-around where I can use a setTimeout which I have commented in the function, but I'd rather do this correctly in Aurelia events and have a guarantee that the DOM has finished.
I believe I have 2 solutions to this problem which I'm satisfied with and will post them here.
The first is the recommendation above by Fabio to use the microTaskQueue.
Another solution is to use a custom bindable event to invoke the function on completion of the repeat.for on the table here...
<template>
<require from='../tablesorter-bind'></require>
<section id="TasksByClientDiv" class="SmallDivPanel ui-draggable BIBulletinSection100 SmallDivSection hellfire">
<small-div-header smalldivinfo.bind="smallDivObj"></small-div-header>
<div id="TasksByClientBox">
<div style="margin-top: 10px;font-size: 20px;">Frequency: ${freq} / Period: ${filterVal}</div>
<div id="TasksByClientTableDiv" class="SmallDivMainPanel">
<table id="TasksByClientTable" >
<thead class="tablesorter">
<tr>
<th>Client</th>
<th>Received</th>
<th>Prepped</th>
<th>Loaded</th>
<th>Doc Loaded</th>
</tr>
</thead>
<tbody>
<tr click.trigger="OpenDetailsDiv(x)" repeat.for="x of tasksByClient" table-id.bind="tableId">
<td>${x.Client}</td>
<td>${x.totLoaded}</td>
<td>${x.totLoaded}</td>
<td>${x.totPrepped}</td>
<td>${x.numDocLoaded}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</template>
where tableId is defined in the View-Model as my tableID
I then setup the custom element like so:
tablesorter-bind.js
import {inject, customAttribute, TaskQueue} from 'aurelia-framework';
#customAttribute('table-id')
#inject(Element, TaskQueue)
export class TablesorterBind {
constructor(element, taskQueue) {
// "element" will be the DOM element rendered from the template
this.element = element;
this.taskQueue = taskQueue;
}
attached() {
}
bind(bindingContext, overridingContext) {
if (overridingContext.$last === true) {
this.taskQueue.queueMicroTask(
() => {
//This is the jQuery update call to the tablesorter function
$('#' + this.value).trigger('update');
}
);
}
}
}

Aurelia - Watch Dependency Value for Change

Suppose you have a class you are injecting into a another class or component. Is there a way to watch for changes on an attributed of the dependency you are injecting and act upon it?
For example, say you have the following app:
app.html
<template>
<input type="text" value.bind="item">
<button click.trigger="addToList()">Add</button>
<h3>Modded</h3>
<ul>
<li repeat.for="it of modded">${it}</li>
</ul>
<h3>Original</h3>
<ul>
<li repeat.for="it of dep.items">${it}</li>
</ul>
</template>
app.js
import {bindable, inject} from 'aurelia-framework';
import {Dep} from './dep';
#inject(Dep)
export class App {
constructor(dep) {
this.dep = dep;
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
dep.js
export class Dep {
constructor() {
this.items = ['one', 'two', 'three'];
}
addItem(item) {
this.items.push(item);
}
}
Now, let's say that some other component modifies Dep.items. Is there a way to watch for changes in app.js on this.dep.items and then call modifyItems()?
Assume modifyItems() is more complex than this example so maybe a value converter is not the best option. (unless it is the only option I guess)
Here is working plunker with the above example: http://plnkr.co/edit/rEs9UM?p=preview
Someone pointed me to the BindingEngine.collectionObserver and it appears that is what I needed.
app.js:
import {inject} from 'aurelia-framework';
import {BindingEngine} from 'aurelia-binding';
import {Dep} from './dep';
#inject(Dep, BindingEngine)
export class App {
constructor(dep, bindingEngine) {
this.dep = dep;
let subscription = bindingEngine.collectionObserver(this.dep.items)
.subscribe((newVal, oldVal) => {
console.debug(newVal, oldVal);
this.modifyItems();
});
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
this.item = '';
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
Here is the working pluker: http://plnkr.co/edit/Pcyxrh?p=preview