onChange on dropdown is not working with form control - html

I've a problem with my dropdown. I have several values inside and like to use the selected one. This is my code, which is currently not working. The FormControll is always empty:
app.component.html
<custom-dropdown [formControl]="majorAreaCtrl" (onChange)="onChange()" [label]="'EXPERTS.MAJOR_AREAS'|translate">
<custom-dropdown-item *ngFor="let area of areas" [value]="area.key">
{{ area }}
</custom-dropdown-item>
</custom-dropdown>
app.component.ts
export class ExpertsComponent implements OnInit {
readonly majorAreas = MajorArea
readonly areas = Object.values(this.majorAreas)
readonly majorAreaCtrl = new FormControl()
ngOnInit(): void {
console.log(this.majorAreas, this.allExperts)
}
onChange() {
console.log(this.majorAreaCtrl.value)
}
} 
Do anyone see my mistake? Thank you in advance!!

You actually don't need the onChange listener (the [formControl] binding handles that).
However, this requires that your custom-dropdown component implements the ControlValueAccessor interface.
If that's the case, you should be able to respond to changes by subscribing to the this.majorAreaCtrl.valueChanges Observable (for instance).
Here's a small example, albeit without your custom component.
If your component doesn't implement ControlValueAccessor, I (and many others here) would be happy to help you implement it.

Related

How to access HTML elements within an Angular service

My app.component includes a component comp1.
comp1.component.html includes a button defined as shown below
<button #myButton (click)='function()'> Temporary </button>
<button #myButton2 (click)='function2()'> Temporary2 </button>
In comp1.component.ts i define a ViewChild as follows:
#ViewChild('myButton') myButton !: ElementRef;
#ViewChild('myButton2') myButton2 !: ElementRef;
I have a service called nlp-service within which i would like to reference myButton and mybutton2 so that i can call the click() event. My reasoning for doing the click through a service is because the service will be used to mainly trigger clicks on different buttons based on the input i provide to the service. So which buttons i click will be dependent on the service.
I am unable to refer to the myButton in the service.
Any solutions or rather any other approaches that are better suited for this will be well appreciated. I am extremely new to Web Dev and Angular so would like a thorough answer.
Because of nature of templates in angular, you can not have access to elements in the template until ngAfterViewInit hook, so one of the way to solve this problem is to call nplService in ngAfterViewInit and set buttons elemenets
export class Component {
#ViewChild('myButton') myButton !: ElementRef;
#ViewChild('myButton2') myButton2 !: ElementRef;
constructor(
private nplService: NplService,
) {}
ngAfterViewInit(): void {
this.nplService.setButtons({
myButton: this.myButton,
myButton2: this.myButton2,
})
}
}

is there a state management solution for lit-element?

If there is a list which should be rendered from an array, and the array will be passed from the grand-grand-grand-grand-grand-parent custom element. That will be super annoying.
Is there a global state management solution for lit-element, just like redux?
Yes, check out LitState (npm package name lit-element-state).
I created this specially for LitElement. It has the same mindset: simple, small and powerful.
Because it is created specially for LitElement, it integrates very well and therefore the usage is very simple. You make a state object like this:
import { LitState, stateVar } from 'lit-element-state';
class MyState extends LitState {
#stateVar() myCounter = 0;
}
export const myState = new MyState();
Usage without #decorators, look here.
Then you can use the state in your components like this:
import { LitElement, html } from 'lit-element';
import { observeState } from 'lit-element-state';
import { myState } from './my-state.js';
class MyComponent extends observeState(LitElement) {
render() {
return html`
<h1>Counter: ${myState.counter}</h1>
<button #click=${() => myState.counter++}></button>
`;
}
}
When you add the observeState() mixin to your component, the component will automatically re-render when any stateVar they use changes. You can do this with any amount of component and states and it will all automatically stay synchronized thanks to the observeState() mixin.
LitElement is a library and you can use any library for state management that you want. Just subscribe to the store in the constructor or connectedCallback (unsubscribe in disconnectedCallback) and change the components' properties when the store notifies you of a change.
Here you have some PWA helpers that works with litElement and you have one for Redux.
https://github.com/Polymer/pwa-helpers#connect-mixinjs
I am late in the game, but this can be quite usefull:
https://www.npmjs.com/package/#lit-app/state
#lit-app/state is a global state management, integrating with lit web-components.
Why a new state-management tool ?
There are plenty options available for state management, so why yet another one?
Some existing options are too heavy. In my opinion, managing state should be lean and simple. Redux, for instance falls into this category.
Some solutions designed for lit (for instance lit-state) do not support Typescript and do not take advantage of lit#2 Reactive Controlers, very well suited for hooking external features into templating lifecyce.
Some elegant ideas were worth pursuing (for instance this tweet, or this post.
How to use it?
import { State, StateController, property } from "#lit-app/state";
import { LitElement } from "lit";
// declare some state
class MyState extends State {
#property({value: 'Bob'}) name
}
const myState = new MyState()
// declare a component
class StateEl extends LitElement {
// StateController is a Reactive Controller binding myState with the element
state = new StateController(this, myState)
override render() {
return html`
<div>This will be updated when the state changes: ${myState.name}</div>
`;
}
}
// changing the state will reflect in the template
myState.name = 'Alice'
I'd look into MobX which is an extremely popular framework independent state management library
"MobX is unopinionated and allows you to manage your application state outside of any UI framework. This makes your code decoupled, portable, and above all, easily testable." - (Github)

Angular | Run event on other button

(Invalid question. You do not need to refer to it. Thank you.)
Solution : Use a common variable not $event.
I want to run the event on the other buttons.
I use $event, but I can not call function immediately.
.html
<p-fileUpload (onSelect)="runSelect($event)"
accept="image/*" #imageUpload></p-fileUpload>
<button (click)="callImageSelect()" #button2></button>
When button2 is pressed, I want to run (onSelect) event on #imageUpload.
(onSelect) have a parameter. (event.files: List of selected files)
If press button1 to run ,
and I want to put the default value in $event.
So the way I found is to use viewChild.
.ts
#ViewChild('imageUpload') imageUpload;
...
callImageSelect() {
this.imageUpload.**onSelect()**; // In this section, call (onSelect).
}
runSelect(event) {
event.files.push(defalut);
// Here I put the default value for event.
}
Is there a good way?
Thank you for your advice.
What you want is to trigger event onSelect. The point to be noted here is onSelect is not a function but event. You should not call the event instead call the function which actually triggers it.
Since you are using onSelect which is custom event so you must be using some Directive for the same. So what you should do is, access the Directive inside the component and call the function which triggers the event (EventEmitter);
Sue-do code
#Directive(selector="my-directive")
public class MyDirective {
value : string;
onSelect = new EventEmitter();
public selectionMade(){
this.onSelect.emit(value);
}
}
Component html
<button (onSelect)="runSelect($event)" myDirective #myDirective="myDirective"></button>
<button (click)="clickTest()" #button2></button>
Component ts
#ViewChild('myDirective') myDirective : MyDirective;
...
clickTest() {
this.myDirective.selectionMade(); // In this section, call (onSelect).
}

Create toggle with CSS and HTML with input variable Angular 2

I have created an Angular component that displays a toggle-switch. The functionalities of the switch are what I want, i.e. it switches correctly when I click on it. The only extra thing I want to add, is that the initial state is based on a boolean Input variable enableSwitch.
So when I display the toggle switch like this:
<toggle-switch [enableSwitch]="true"></toggle-switch>
I want initially the toggle being enabled (rectangle on the right). When I click on it, I want to toggle to the disabled state, and I want to return the boolean as false.
The same function applies when I want to display the toggle initially being disabled:
<toggle-switch [enableSwitch]="false"></toggle-switch>
I have created a Plunker to demonstrate the problem.
Can anyone help me further?
You can do a two-way binding approach, using ngModel.
<input type="checkbox" [(ngModel)]="enableSwitch">
To instanciate the variable as true, you could do:
#Input() enableSwitch: boolean = true;
The click event would then be obsolete.
Rememeber to include the FormsModule in app.ts.
See forked example:
https://plnkr.co/edit/TIxr6BGrXeVmPYO35DrQ?p=preview
EDIT:
In case of two-way databinding on the component - https://angular.io/guide/template-syntax#two-way-binding---
You first need the banana-in-a-box syntax:
<toggle-switch [(enableSwitch)]="enable"></toggle-switch>
Then you need to use an EventEmitter with the special variable name fooChange where foo is the #Input name.
#Output() enableSwitchChange = new EventEmitter<boolean>();
#Input() enableSwitch: Boolean = true;
Then emit the updated value when the value changes:
update() {
this.enableSwitchChange.emit(this.enableSwitch);
}
To call the update() function you can use change.
<input type="checkbox" [(ngModel)]="enableSwitch" (change)="update()">
Plunkr: https://plnkr.co/edit/Lx7QDbKt6V3HdFJN3bZx?p=preview
This can also be solved simpler without ngModel, but you probably see how yourself. =)

Passing a function to another function on a separate component in Ionic 2 or 3

a bit of a tricky one here. I've been trying to wrap my head around this one with no success. Ok, here it goes... I've got a function in a different ionic component set out like this:
home.ts
playAudio() {
this.streamingMedia.playAudio('MY-URL');
this.play = false;
}
stopAudio() {
this.streamingMedia.stopAudio();
this.play = true;
}
Then I set up this boolean within the same home.ts file to toggle between the play and pause buttons
play: boolean = true;
Now, in my home.html file I have it setup as follows:
home.html
<div *ngIf="play">
<img src="assets/img/play.png" class="play" (tap)="playAudio()" />
</div>
<div *ngIf="!play">
<img src="assets/img/pause.png" class="play animated flip"
(tap)="stopAudio()" />
</div>
Essentially what is happening here is, when I click the play button, play starts and then it shows the pause button. And likewise, when I click the pause button, play stops and shows the play button.
So, here is my problem, I'm trying to call the function in a different page/component. I want it to automatically play the stream when I open the page, I tried doing that by writing the same playAudio() function (as in the home.ts file) in the app.component.ts file where I need to set the function:
app.component.ts
play: boolean = true;
constructor() {
this.playAudio(); }
playAudio() {
this.streamingMedia.playAudio('MY_URL');
this.play = false;
}
but when the page opens, the stream plays but I then don't have the option to set the pause button in place of the play button.
I hope I'm making sense.
First, a suggestion:
On your playAudio() function in home.ts, set this.play = true and vice versa in stopAudio(). In your html, do a checkpoint for !play instead of play and you have a sensible logic that can be understood by anybody else that reads your code.
For your dilemma, I'd suggest using an angular service injectable
Basically, create your service. Decorate it as an injectable. Reference it as a provider in your app.module.ts and inject it into your app.component.ts's and home.ts's constructor. write and retrieve your playAudio() and stopAudio() in and from that service.
something like this:
import {Injectable} from '#angular/core';
import {StreamingMedia} from '#ionic-native/streaming-media';
#Injectable()
export class PlayerService {
playing: boolean;
audioUrl: string = //whatever url;
//Inject your dependencies here
constructor(public streamingMedia: StreamingMedia){
this.playing = false;
}
playAudio(){
this.streamingMedia.playAudio(this.audioUrl);
this.playing = true;
}
stopAudio(){
this.streamingMedia.stopAudio();
this.playing = false;
}
}
reference this as a provider in your app.module.ts under providers section: providers: [PlayerService]
In your home.ts's constructor, inject the service you just made
constructor(public playerService: PlayerService){}
To use the playAudio() and stopAudio() function, just type
this.playerService.playAudio() and so on. Use the playing property of the service to tie up with the html.
I hope that clears things up with injectables.
In your app.component.ts, inject the service in the constructor as I showed before. Implement OnInit interface as
export class AppComponent implements OnInit {} and then create a ngOnInit(){} function where you can call the service's playAudio() function to start playing on initialization. And use service's playing property to again tie up with the UI.