Opening error popover from service in Angular 2 - angularjs

I would like to create a service which will be responsible for opening bootstrap popovers with errors and success communicates. I have two components ErrorComponent, SuccessComponent and one service called CommunicatesComponent. I would like to use this service to open popover like
service.error('some error')
service.success('success!')
And this should display popover with provided text as argument. What I am doing is setting component property in service like followed and use this property in this service:
ErrorComponent
export class ErrorComponent implements OnInit {
public text:string;
#ViewChild('errorPopover') private errorPopover: NgbPopover;
constructor(private communicatesService:CommunicatesService) {
}
public ngOnInit() {
this.communicatesService.setErrorComponent(this)
}
}
Service:
#Injectable()
export class CommunicatesService {
private errorComponent:ErrorComponent
public setErrorComponent(component:ErrorComponent) {
this.errorComponent = component;
}
public error(text:string) {
console.log(this.errorComponent)
// this.errorComponent.text = text;
}
}
Unfortunitelly, it seems that my component object is not provided well, because console log prints undefined. How it should be done?
Regards

There are two things I would change in your design
ErrorComponent and CommunicatesService depends on each other. It's good to avoid it - CommunicatesService could easily work with different components. So you could create an rx Observable public property of the service, so any component can subscribe to it. When service.success('success!'), the service will send the message text to the subscribers.
In ErrorComponent, you get the popover component as a #ViewChild. You could consider binding ErrorComponent.text to the popover directly (reversing the dependency).
These changes could solve the problems you have and make the design looser - easier to understand and maintain.

Related

TypeScript 2: Calling method of class, not object

I have found a great service for recording audio in Ionic.
But there is at least one thing that I do not understand:
For example, in line 25:
this.MediaPlugin.startRecord();
Question: Why does it call on this.MediaPlugin.startRecord() and not this.mediaPlugin.startRecord() where this.mediaPlugin is an object and MediaPlugin a class?
If the class uses this.MediaPlugin to do the actions why does it return in the get method an object?
complete code:
import { Injectable } from '#angular/core';
import { MediaPlugin } from 'ionic-native';
export enum AudioRecorderState {
Ready,
Recording,
Recorded,
Playing
}
#Injectable()
export class AudioRecorder {
mediaPlugin: MediaPlugin = null;
state: AudioRecorderState = AudioRecorderState.Ready;
get MediaPlugin(): MediaPlugin {
if (this.mediaPlugin == null) {
this.mediaPlugin = new MediaPlugin('../Library/NoCloud/recording.wav');
}
return this.mediaPlugin;
}
startRecording() {
this.MediaPlugin.startRecord();
this.state = AudioRecorderState.Recording;
}
stopRecording() {
this.MediaPlugin.stopRecord();
this.state = AudioRecorderState.Recorded;
}
startPlayback() {
this.MediaPlugin.play();
this.state = AudioRecorderState.Playing;
}
stopPlayback() {
this.MediaPlugin.stop();
this.state = AudioRecorderState.Ready;
}
}
this.MediaPlugin is the reference to the get. Using this syntax, it'll construct the MediaPlugin on the first call, but use the constructed on following calls.
(This solutions seems a little weird to me too, since it'd make more sense to just use the constructor in the AudioRecorder class to init this.mediaPlugin, and then use this.mediaPlugin elsewhere)
First of all following method is a getter and I guess it's an attempt to implement readonly property with lazy initialization.
get MediaPlugin(): MediaPlugin {
if (this.mediaPlugin == null) {
this.mediaPlugin = new MediaPlugin('../Library/NoCloud/recording.wav');
}
return this.mediaPlugin;
}
So when you try to access this.MediaPlugin for the first time, new instance of MediaPlugin is being created. And when you instantiate AudioRecorder and do not use it (for some reasons) it helps you to save memory not creating instance of MediaPlugin immediately. (see Lazy loading pattern)
Answering you question:
Why does it call on this.MediaPlugin.startRecord() and not
this.mediaPlugin.startRecord() where this.mediaPlugin is an object and
MediaPlugin a class?
Typescript does not provide any common mechanism of lazy loading and the way to implement it is to create private property with a getter method (as in your AudioRecorder class).
Calling this.MediaPlugin.startRecord() you kind of encapsulating creation and manipulation logic of MediaPlugin instance.
Naming conventions
Someone messed up with regards to naming conventions. The name of the property should be mediaPlugin, not MediaPlugin.
The reason that the property accessor is called MediaPlugin is that there is already a backing field called mediaPlugin.
Some would argue that the backing field should be called _mediaPlugin. Others would argue that this would go against naming conventions. If the latter is the case, you could call it mediaPluginField.
class AudioRecorder {
mediaPluginField: MediaPlugin = null;
get mediaPlugin(): MediaPlugin {
if (this.mediaPluginField === null) {
this.mediaPluginField = new MediaPlugin('../Library/NoCloud/recording.wav');
}
return this.mediaPluginField;
}
}
Dependency inversion
However, as #Vladyslav Yefremov points out, an arguably better option would be to inject the MediaPlugin dependency through the constructor or pull it from some kind of service locator.
class AudioRecorder {
constructor(private mediaPlugin: MediaPlugin) { }
}
or
class AudioRecorder {
private mediaPlugin: MediaPlugin;
constructor(mediaPlugin: MediaPlugin) {
this.mediaPlugin = mediaPlugin;
}
}
Lazy instantiation
I do not immediately see the need to instantiate the media plugin lazily, as the audio recorder needs a media plugin for all actions.
However, it could be because the media plugin has the side effect of loading the media file when instantiated. If this is the case, the media plugin property is instantiated lazily to delay the opening of the resource until it is actually needed, i.e. when the AudioRecorder is needed.

Angular 2 - Plug in dynamic data from API with parameter to HTML?

I'm fairly new to Angular 2, and I'm trying to find the best way to pass data to a nested component, call a method (REST API) using that data, and return the result into the parent HTML.
I understand how to pass the data - just use the #Input decorator on a variable in the child component. That gets the data in. Now I want to process that data and compute a result. I can't do that in the constructor of the child component, because in the constructor, the input value has not yet been passed. So where do I do it? Is there a render event I can use? I arrived at a solution that seems to work very well, I just don't know if I'm breaking some rules or causing issues.
Since I'm using Typescript, I can use setters. I defined a setter for the input value, so in the setter I can run code to process the data. Here is my working code:
import { Component, Input } from "#angular/core";
import { Http } from "#angular/http";
import { AppService } from "./../../services/appservice";
import { UserProfileService } from "./../../services/userprofileservice";
#Component({
selector: "displayname",
template: "{{displayName}}",
providers: [AppService, UserProfileService]
})
export class DisplayName {
public displayName: string;
private profiler: UserProfileService;
constructor(private appService: AppService) {
this.profiler = new UserProfileService(this.appService);
}
#Input("login")
set login(newLogin: string) {
this.profiler.getUserProfile(newLogin, (profile) => {
this.displayName = profile ? profile.displayName : newLogin;
});
}
}
What this code does is take a login name as input, fetch the user's display name, and set the displayName variable to the result. If the result is null, it just returns the login name. Using the #Input decorator on the setter works great, I've just never seen it done before in any examples so I don't know if there is a better way.
My HTML code looks like this (simplified for this example):
<tr *ngFor="let user of userList>
<td><displayname [login]="user.loginName"></displayname></td>
</tr>
Another way to ask this question is to back up to my basic use case, which I think must be very common. How do you dynamically insert data from a REST API into an HTML page? In this case I want to pass in the login name, and render the Display Name, that is fetched from a REST API. In this case I don't need any HTML formatting (you can see my template is just a variable, no HTML) - is there a simpler way to do this? Should I be using an #Output?
Thanks,
Randy

Accessing current controller instance (`this`) in events

We are using TypeScript with angularjs 1.5. We have a situation where we need to trigger an event to load data from the parent component. The problem we are facing is that when the event is triggered, this is referring to the window object not the controller in which it is present. How to get the current instance in the event?
Because it is TypeScript any method or property can be accessed though this only. And I need the controller instance because there are few initialization done in the constructor which I need to access.
Example implementation
namespace doc.common {
#Component({
selector: name
})
export class myComponent {
#bind.oneWay()
public id:number;
public service:myService;
/*ngInject*/
constructor(private serviceRef:myService){
this.service = serviceRef;
}
public $onChange():void {
//*this* is referred to window object, not controller instance
}
}
}
Usage
<my-component id='id'></my-component>

Correct way to use libraries like jquery/jqueryui inside angular 2 component

I have researched this topic a bit and found out about typings for libraries that need to be used for typescript. What I struggled to find is examples of usage, lets say for jquery inside an angular 2 application.
here are some questions:
1) Where would one write his jQuery code, is it inside class or inside constructor of that class?
2) Do we need to use document.ready at any point to wrap jQuery code? i.e. if we write code inside constructor is it run after this event?
few examples of usage, is one of these correct?
example 1
export class MyApp {
constructor() {
$('.mydiv').hide();
}
}
example 2
export class MyApp {
constructor() {
}
$('.mydiv').hide();
}
example 3
export class MyApp {
constructor() {
}
$( document ).ready(function() {
$('.mydiv').hide();
}
}
Ideally you should wait till component content get initialized, in order to make the DOM available on which you wanted to apply jQuery. For that you need to use AfterViewInit which is one of hook of angular2 lifecycle.
You need to implement AfterViewInit on a class and write add ngAfterViewInit method to get notify whenever component content gets ready.
import { AfterViewInit } from 'angular2/core';
export class MyApp implements AfterViewInit {
constructor() {
}
ngAfterViewInit(){
//here you will have code where component content is ready.
$('.mydiv').hide();
}
}
I'm going to start by answering your second question. Yes, you should use document.ready because its intention is to wait until the DOM is loaded. If it's not loaded, your jQuery code won't work.
Now to answer your first question, once loaded, it shouldn't matter where you call jQuery.
See this.

TypeScript: inherit from Array

I'm trying to create a rich data model for an AngularJS application and I would like for my model to inherit from Array<BaseModel>. I haven't found a way to do this (with confidence) yet.
In pseudocode this would be something like:
// NOT REAL CODE. DOES NOT WORK
class BaseModel {}
class Person extends BaseModel {}
class People extends Array<Person> {
constructor(private promise:Promise) { }
$unwrap(promise) {
promise.then((response) => {
response.data.map((person) => {
this.push(new Person(person));
});
});
}
static $load() {
/* do some ajaxy thing and unwrap the promise right onto the
the new instance of this rich model */
return new People(promise);
}
}
The reason I would like something like this is that now I can bind this model directly to my view and get updates on the fly.
Any thoughts on extending from Array?
At this point it time it is not possible to inherit from Array in TypeScript. It is best to unwrap into a public array field and use this field to bind to the UI. It resolves into the same functionality and remove the necessity of inheriting from Array.

Resources