Angular 2 Base Class Output EventEmitter doesn't get raised or handled - event-handling

Very simple base class Closer
import {EventEmitter, Output} from 'angular2/core';
export class Closer {
#Output() closed: EventEmitter<any> = new EventEmitter();
constructor() {}
close() {
this.closed.emit({});
}
}
If I extends Closer another class and then mark it up with
<derived-class (closed)="closeHandler()"></derived-class>
closeHandler() will never get called. I can see Closer.close() getting called but the emit doesn't get propagated by the derived class or handled by the class whose template contains the derived class and event binding.
If I simply move the #Output to the derived class it works. But it would seem that Angular2 should be putting that on derived classes. The ability to fully define a set of behavior and inherit it would be nice.

I had the same problem this morning and just figured out how to do it. All you have to do is add outputs: ['closed'] to the components section of the derived class that is extending Closer and you have to remove #Output() from the closed declaration inside the Closer class
Here is a quick demo based on your example:
Derived Class:
#Component({
selector: 'derived-class',
outputs: ['closed']
})
export class DerivedClass extends Closer {}
Closer Class:
import {EventEmitter} from 'angular2/core';
export class Closer {
closed: EventEmitter<any> = new EventEmitter();
constructor() {}
close() {
this.closed.emit({});
}
}

Related

CommonJS: require class and extend class

I'm a bit struggling with the JavaScript classes (CommonJS).
I've got a class in a javascript 'module' which I can import to another js file using:
DucoMaster.js:
class DucoMaster {
constructor(node){
this.node = node;
}
}
module.exports = {DucoMaster}
DucoModules.js:
const {DucoMaster} = require("./DucoMaster");
...
let test = new DucoMaster;
console.log(test);
It builds and printing the test works, it prints the object as defined as class in DucoMaster.
Now I would like to import the module and use it to extend another class like:
'class DucoMaster extends ParentClass' within DucoModules.js
Is this possible with DucoModules.js?
Best regards,

How can I control lifecycle of extended class instances in Angular 2

In Angular 2.4.9 this code below does not work for me:
export class MyComponent extends BaseComponent implements OnInit {
type = 2;
constructor() {
super();
}
ngOnInit(){
console.log('inited type', this.type)
}
}
meaning: there's no console output - regardless of whether BaseComponent implements OnInit or not.
If BaseComponent implements the hook though, then only its hooks are called, not the overriden one on MyComponent.
As the discussion proved that the plunker created alongside my description actually works, I managed to reproduce my problem in this plunkr.
Is there any way to use lifecycle hooks in the extended class?
Problem is your type is never set so your *ngIf's are not working.
According to the plunker you've provided, you need to provide the types in an #Input
app template
<base-comp [type]="1"></base-comp>
<base-comp [type]="2"></base-comp>
<base-comp [type]="1"></base-comp>
<base-comp [type]="2"></base-comp>
BaseComponent field
#Input() public type: number;
Fixed plunker: http://plnkr.co/edit/B4dFDR1bgLI5kGgGrozE?p=preview

How do I declare a model class in my Angular 2 component using TypeScript?

I am new to Angular 2 and TypeScript and I'm trying to follow best practices.
Instead of using a simple JavaScript model ({ }), I'm attempting to create a TypeScript class.
However, Angular 2 doesn't seem to like it.
My code is:
import { Component, Input } from "#angular/core";
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>"
})
export class testWidget {
constructor(private model: Model) {}
}
class Model {
param1: string;
}
and I'm using it as:
import { testWidget} from "lib/testWidget";
#Component({
selector: "myComponent",
template: "<testWidget></testWidget>",
directives: [testWidget]
})
I'm getting an error from Angular:
EXCEPTION: Can't resolve all parameters for testWidget: (?).
So I thought, Model isn't defined yet... I'll move it to the top!
Except now I get the exception:
ORIGINAL EXCEPTION: No provider for Model!
How do I accomplish this??
Edit: Thanks to all for the answer. It led me to the right path.
In order to inject this into the constructor, I need to add it to the providers on the component.
This appears to work:
import { Component, Input } from "#angular/core";
class Model {
param1: string;
}
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>",
providers: [Model]
})
export class testWidget {
constructor(private model: Model) {}
}
I'd try this:
Split your Model into a separate file called model.ts:
export class Model {
param1: string;
}
Import it into your component. This will give you the added benefit of being able to use it in other components:
Import { Model } from './model';
Initialize in the component:
export class testWidget {
public model: Model;
constructor(){
this.model = new Model();
this.model.param1 = "your string value here";
}
}
Access it appropriately in the html:
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>"
})
I want to add to the answer a comment made by #PatMigliaccio because it's important to adapt to the latest tools and technologies:
If you are using angular-cli you can call ng g class model and it will generate it for you. model being replaced with whatever naming you desire.
The problem lies that you haven't added Model to either the bootstrap (which will make it a singleton), or to the providers array of your component definition:
#Component({
selector: "testWidget",
template: "<div>This is a test and {{param1}} is my param.</div>",
providers : [
Model
]
})
export class testWidget {
constructor(private model: Model) {}
}
And yes, you should define Model above the Component. But better would be to put it in his own file.
But if you want it to be just a class from which you can create multiple instances, you better just use new.
#Component({
selector: "testWidget",
template: "<div>This is a test and {{param1}} is my param.</div>"
})
export class testWidget {
private model: Model = new Model();
constructor() {}
}
export class Car {
id: number;
make: string;
model: string;
color: string;
year: Date;
constructor(car) {
{
this.id = car.id;
this.make = car.make || '';
this.model = car.model || '';
this.color = car.color || '';
this.year = new Date(car.year).getYear();
}
}
}
The || can become super useful for very complex data objects to default data that doesn't exist.
.
.
In your component.ts or service.ts file you can deserialize response data into the model:
// Import the car model
import { Car } from './car.model.ts';
// If single object
car = new Car(someObject);
// If array of cars
cars = someDataToDeserialize.map(c => new Car(c));
In your case you are having model on same page, but you have it declared after your Component class, so that's you need to use forwardRef to refer to Class. Don't prefer to do this, always have model object in separate file.
export class testWidget {
constructor(#Inject(forwardRef(() => Model)) private service: Model) {}
}
Additionally you have to change you view interpolation to refer to correct object
{{model?.param1}}
Better thing you should do is, you can have your Model Class define in different file & then import it as an when you require it by doing. Also have export before you class name, so that you can import it.
import { Model } from './model';
my code is
import { Component } from '#angular/core';
class model {
username : string;
password : string;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
username : string;
password : string;
usermodel = new model();
login(){
if(this.usermodel.username == "admin"){
alert("hi");
}else{
alert("bye");
this.usermodel.username = "";
}
}
}
and the html goes like this :
<div class="login">
Usernmae : <input type="text" [(ngModel)]="usermodel.username"/>
Password : <input type="text" [(ngModel)]="usermodel.password"/>
<input type="button" value="Click Me" (click)="login()" />
</div>
You can use the angular-cli as the comments in #brendon's answer suggest.
You might also want to try:
ng g class modelsDirectoy/modelName --type=model
/* will create
src/app/modelsDirectoy
├── modelName.model.ts
├── ...
...
*/
Bear in mind:
ng g class !== ng g c
However, you can use ng g cl as shortcut depending on your angular-cli version.
I realize this is a somewhat older question, but I just wanted to point out that you've add the model variable to your test widget class incorrectly. If you need a Model variable, you shouldn't be trying to pass it in through the component constructor. You are only intended to pass services or other types of injectables that way. If you are instantiating your test widget inside of another component and need to pass a model object as, I would recommend using the angular core OnInit and Input/Output design patterns.
As an example, your code should really look something like this:
import { Component, Input, OnInit } from "#angular/core";
import { YourModelLoadingService } from "../yourModuleRootFolderPath/index"
class Model {
param1: string;
}
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>",
providers: [ YourModelLoadingService ]
})
export class testWidget implements OnInit {
#Input() model: Model; //Use this if you want the parent component instantiating this
//one to be able to directly set the model's value
private _model: Model; //Use this if you only want the model to be private within
//the component along with a service to load the model's value
constructor(
private _yourModelLoadingService: YourModelLoadingService //This service should
//usually be provided at the module level, not the component level
) {}
ngOnInit() {
this.load();
}
private load() {
//add some code to make your component read only,
//possibly add a busy spinner on top of your view
//This is to avoid bugs as well as communicate to the user what's
//actually going on
//If using the Input model so the parent scope can set the contents of model,
//add code an event call back for when model gets set via the parent
//On event: now that loading is done, disable read only mode and your spinner
//if you added one
//If using the service to set the contents of model, add code that calls your
//service's functions that return the value of model
//After setting the value of model, disable read only mode and your spinner
//if you added one. Depending on if you leverage Observables, or other methods
//this may also be done in a callback
}
}
A class which is essentially just a struct/model should not be injected, because it means you can only have a single shared instanced of that class within the scope it was provided. In this case, that means a single instance of Model is created by the dependency injector every time testWidget is instantiated. If it were provided at the module level, you would only have a single instance shared among all components and services within that module.
Instead, you should be following standard Object Oriented practices and creating a private model variable as part of the class, and if you need to pass information into that model when you instantiate the instance, that should be handled by a service (injectable) provided by the parent module. This is how both dependency injection and communication is intended to be performed in angular.
Also, as some of the other mentioned, you should be declaring your model classes in a separate file and importing the class.
I would strongly recommend going back to the angular documentation reference and reviewing the basics pages on the various annotations and class types:
https://angular.io/guide/architecture
You should pay particular attention to the sections on Modules, Components and Services/Dependency Injection as these are essential to understanding how to use Angular on an architectural level. Angular is a very architecture heavy language because it is so high level. Separation of concerns, dependency injection factories and javascript versioning for browser comparability are mainly handled for you, but you have to use their application architecture correctly or you'll find things don't work as you expect.
create model.ts in your component directory as below
export module DataModel {
export interface DataObjectName {
propertyName: type;
}
export interface DataObjectAnother {
propertyName: type;
}
}
then in your component import above as,
import {DataModel} from './model';
export class YourComponent {
public DataObject: DataModel.DataObjectName;
}
your DataObject should have all the properties from DataObjectName.

TypeScript: Access global type hidden by local class definition?

Lets assume i have a class which has the same name as an previously defined type which is defined inside lib.d.ts. How would i make use of that type within this class.
For example, i have the class Event, which has to deal with the browsers Event object, which is defined as an interface in lib.d.ts.
export class Event { // own definition of Event which hides original Event
public dealWithBrowserEvent(event: Event): void { // Event object defined by lib.d.ts
// deal with it
}
}
How would i tell Typescript that this are two different types. Of course i could simply rename my class, but i don't want to do that, because the name is perfect for my use case.
You can archive this by doing so:
E.ts:
class CustomEvent
{
public dealWithBrowserEvent(event: Event): void
{
}
}
export default CustomEvent;
A.ts:
import Event from './E'
export class SomeClass
{
//Parameter e here is instance of your CustomEvent class
public someMethod(e: Event): void
{
let be: any;
//get browser event here
e.dealWithBrowserEvent(be)
}
}
More on declaration merging, and what can be merged and what not: link
I strongly recommend you not doing so. This code will lead to a lot of confusion for your colleagues reading/modifying it later, let alone headache of not being able to use in the same file standard class for Event.
In the meantime i found a quite doable solution. I defined an additional module which exports renamed interfaces. If i import this module, i can use the renamed types as if they would be original types.
browser.ts
// Event will be the one from lib.d.ts, because the compiler does not know anything about
// the class Event inside this module/file.
// All defined properties will be inherited for code completion.
export interface BrowserEvent extends Event {
additionalProperty1: SomeType;
additionalProperty2: SomeType;
}
If you don't need additional properties you can just do type aliasing:
browser.ts
// Alias for Event
export type BrowserEvent = Event;
event.ts
import {BrowserEvent} from './browser.ts';
export class Event { // Definition of Event hides original Event, but not BrowserEvent
public dealWithBrowserEvent(event: BrowserEvent): void {
// deal with it
}
}
I'm quite happy with this solution, but maybe there is a even better solution.

AS3 accessing stage from class

Timeline code:
import as3.first;
first._this=this;
var str1:String='this is timeline';
Class code:
package as3 {
import flash.display.MovieClip;
public class first extends MovieClip {
public static var _this:Object;
trace(_this.str1);
}
}
Error message:
TypeError: Error #1009: Cannot access a property or method of a null
object reference.
Trying to wrap my mind around how classes work. Need to access timeline functions and variables from class. What am I doing wrong and how can I make this work?
All in all what you are doing is somewhat weird. May be, you just want a document class for your SWF root? You as well could add a class to any movieclip in your library: both ways grant you access to timeline.
package as3
{
import flash.display.MovieClip;
public class first extends MovieClip
{
public static var _this:Object;
trace(_this.str1); // you may place code here... but consider this:
// this area is STATIC, the code here
// executes only once when class gets initialized,
// so, this happens BEFORE you assign first._this=this;
}
}