How to access HTML elements within an Angular service - html

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,
})
}
}

Related

onChange on dropdown is not working with form control

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.

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).
}

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.

AS3 [Event(name="", type="")], what is the significance?

I develop with FlashDevelop3 R2 and the Flex 3.3 SDK and there are many occasions where I must use the embed metadata tag as such:
[Embed(source="path/to/file")]
private var Asset:Class;
I understand the above all well and good, and I am thankful it exists because I do not like to open the flash IDE too often.
When I am going through other authors classes lately, I have found an interesting metadata tag that I do not understand:
[Event(name="", type="")]
I have yet to see a situation where I require this, and furthermore I really just do not understand what it is for.
Thank in advance for your help.
Brian Hodgeblog.hodgedev.com hodgedev.com
These [Event(name, type)] declarations describe which events a class instance is likely to dispatch.
They are actually useful for code completion - for instance when you type: mySprite.addEventListener(, your code editor (Flex Builder or FlashDevelop) will display a meaningful list of events that this object can dispatch.
So you can add these declarations in your code and benefit from a richer code completion.
Also note that this works with custom Event classes (see FlashDevelop's new Event class template).
package mycomp {
import flash.events.Event;
public class MyEvent extends Event {
public const SOME_EVENT:String = "someEvent";
// required Event type declarations
}
}
package mycomp {
[Event(name="someEvent", type="mycomp.MyEvent")]
public class MyComp extends Sprite {
}
}
package myproject {
import mycomp.MyComp;
public class MyProject {
function MyProject() {
var mc:MyComp = new MyComp();
mc.addEventLister( //completes: SOME_EVENT + Sprite events
}
}
}
We use it for binding custom events to our custom MXML components. This tag allows you to reference it from MXML. See documentation:
[Event(name="enableChanged", type="flash.events.Event")]
class ModalText extends TextArea {
...
}
<MyComp:ModalText enableChanged="handleEnableChangeEvent(event);"/>
The compiler will complain, however, if you try to refer to an event on an mxml tag that was not declared with an event metatag.