Extend Angular2 Directive - javascript

I am trying to extend the angular2-clipboard npm package. I need to get access to its ngOnInit() function and override it to suit a specific use case for copying.
I am new to angular2 and unsure how to do this, the way I've been trying it so far is linked in the plunker below. I am having difficulty since the package is exported as a module called ClipboardModule and I need the directive.
Here is the plugin github and plunker for reference:
Github: https://github.com/maxisam/angular2-clipboard
The file that shows the export is src/clipboard.module.ts
Their github also has a link to a plunker example of it working
My plunker: https://embed.plnkr.co/sIxmFo/

This is how one defines a service, not a directive.
#Injectable()
export default class CopyDirective extends ClipboardModule {
public cm: any; // better way than any?
constructor(cm: ClipboardModule) {
super();
this.cm = cm;
}
}
A directive requires a #Directive() decorator instead of the #Injectable()
Inheritance with components, directives, and pipes isn't supported currently.
I also don't understand why you extend a module when you want to extend a directive. You need to extend the directive class directly.
See also https://github.com/angular/angular/issues/11606
Some scenarios can work though.
Usually you need to repeat all decorators in the subclass.
#Component(), #Directive(), #Input(), #Output(), #ViewChild(ren)(), #ContentChild(ren)(). ...

Related

Databinding in Angular, understanding in detail

I am new to Angular and I just started learning it recently. I came across the concept of Databinding in Angular. I was able to understand the syntax and stuff but there were some questions that I couldn't find an answer for. These are the queries I had:
When we export a class from the component TS file, we can use the class properties in HTML file. For eg: Databinding a class property to a HTML element works. But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
Why exactly are we exporting a class for a component to be used? Is the component a class too? If yes, then wehen we use the component are we calling that class and this leads to rendering the HTML and CSS mentioned in the component?
Please let me know.
Answering your question in details requires having an in-depth knowledge about how Angular internally works, but here's a starting point:
I've generated a component using angular CLI:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
public myProperty: number = 0;
constructor() { }
ngOnInit(): void {
}
}
So:
Is the component a class too?
Yes, as you can see from the labels "export class", your component is first of all a regular JS class, which implements the OnInit interface, has an empty constructor and defines a public variable.
If yes, then when we use the component are we calling that class?
Exactly: Angular does a bit of magic (see the next point of the answer), so whenever finds a html tag named <app-example></app-example>, it creates an ExampleComponent instance and replaces that tag with the html you defined on example.component.html
and this leads to rendering the HTML and CSS mentioned in the component?
The magic happens just above the class definition: Angular heavily relies on Typescript Decorators, which are an (still) experimental feature of Typescript. Decorators allows you (or Angular in our case) to alter the behaviour of a class, for example by intercepting methods call, property changes (did you just say databinding?) and constructor parameters (and this is how Angular's dependency injection works).
In the #Component decorators, which is linked to the below ExampleComponent class, you're defining three things:
the selector, or tag name that Angular will search in the DOM and replace with your component's html
Where to find your component's html, which will be linked to each of your ExampleComponent instance
Stylesheets for your component's html
So, when a property on your component changes (for example myProperty), Angular intercepts that change thanks to the #Component decorators you've defined (to understand how, do a little search about decorators), and will re-render his html. Inserting that property value on a paragraph like <p>{{myProperty}}</p> is just a matter of string replacement.
So, now you have the answer to your first question:
But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
It's not the html that knows which component it belongs, it's the component (or Angular that handles that component) that knows which html has to render and which css needs to apply.
Why exactly are we exporting a class for a component to be used?
This is simply to let Angular know that we have defined a component. Exporting something from a .ts file makes it available on the rest of the project, and particularly on the AppModule file, where you will find your ExampleComponent among the declarations array:
#NgModule({
declarations: [
AppComponent,
ExampleComponent
],
// Something else

how to access DOM elements in angular 4 service?

I am able to access DOM elements components like below
declare var jQuery: any;
declare var $: any;
//component stuff
$('.my_class').innerHeight();
I am trying to implement the same inside the service class, but dom elements and template is not accessible in the service class.
p.s: this is not duplicate of how to access them in components.
You can access DOM from an Angular service using the plain javascript document object, with some little additions to your service:
// We import not only "Injectable", but "Inject" too, from #angular/core
import { Injectable, Inject } from '#angular/core';
// We import DOCUMENT from #angular/common. Be careful, because the old import from '#angular/platform-browser' is deprecated.
import { DOCUMENT } from '#angular/common';
// Our standard service class in the usual way
#Injectable()
export class LoadingSpinnerService {
// In the constructor we inject a dependency to DOCUMENT, of type HTMLDocument
constructor(#Inject(DOCUMENT) private document: HTMLDocument) {
// We create a new div in the DOM, child of the body tag, <div id="LoadingSpinner"></div>
var NewDomElement = this.document.createElement("div");
NewDomElement.setAttribute("id", "LoadingSpinner");
document.body.appendChild(NewDomElement);
}
}
As you will probably know, you crete the service from the command line with something like:
ng g s loading-spinner
Don't forget to edit the app.module.ts to add the "import" to the service, and the item to the "providers" array in its "#NgModule" decorator:
import { LoadingSpinnerService } from './WHATEVER-DIRECTORY-YOU-CREATE-THE-SERVICE/loading-spinner.service';
(...)
providers: [LoadingSpinnerService,
(...)
About the topic of using angular services only for data, i don't agree. As you can read in the official architecture guide for services:
https://angular.io/guide/architecture-services
Service is a broad category encompassing any value, function, or feature that an app needs.
A component can delegate certain tasks to services, such as fetching data from the server, validating user input, or logging directly to the console.
The provided example just in this documentation is for a log data service.
Hope this helps someone.
You can't in services. You can do it javascript way like document.getElementById.
In components and directives You can use ViewChild from #angular/core
HTML:
<div class="my_class" #myElement></div>
TS:
import { ElementRef, ViewChild } from '#angular/core';
#ViewChild('myElement') myElement:ElementRef;
console.log(this.myElement.nativeElement.offsetHeight); // inside any function.
I guess you cannot directly access with CSS selectors by Angular way. Alternatively, you can just use plain old JavaScript
document.getElementsByClassName("my-class");
Note: You can only do this in components and directives not inside services
Why Services
Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service.
Source: https://angular.io/tutorial/toh-pt4
In Simpler terms:
component, directive for presenting, manipulating and interacting with DOM
services are for data handling between your component and backend

Create base class for controller to extend/inherit in Ember application

I'm trying to create a Base Class for my controllers, so I can avoid duplication of code.
The problem here is that it is throwing me errors whenever I try to use it.
"Assertion Failed: You attempted to define a {{link-to "inventory"}} but did not pass the parameters required for generating its dynamic segments. Could not find module controllers/base-inventory imported from frontend/controllers/inventory"
To create my base controller I am using ember cli and this is what I did:
ember g controller base-inventory
Then
// base-inventory.js
const BaseInventory = Ember.Controller.extend({
//my code...
});
export default BaseInventory;
In the controller where I want to use this base class I did the following
import BaseInventory from 'controllers/base-inventory';
// also tried import { BaseInventory } from 'controllers/base-inventory';
// and export default new BaseInventory({});
export default BaseInventory.extend({
//more code here...
});
Any thoughts of what I am doing wrong?
I didn't plan to use mixins, because it doesn't seem the best option here at first. I am not really sure about the sharing content, which mixins provide. I don't think it would be a problem since I'm trying to inherit within controllers, but as I said I'm not sure about how it really works.
If it's not possible to do the way I'm trying to, I'll write a mixin.
Both files are in the same folder structure so import path should be like ./base-inventory
import BaseInventory from './base-inventory';

Writing complex AngularJS directive in typescript

I've found the following directive to select objects from checkboxes:
https://vitalets.github.io/checklist-model/
My problem is that we are using typescript and i have absolutely no idea how to write the given directive in typescript.
I know that the basic style is the following
module myModule {
'use strict';
export function checklistModel(): ng.IDirective {
return {...};
};
};
My problem is that I need the $parse and $compile services to get injected. I've tried to put the code from the directive in the link but I have no idea how to get the directive working.
Could someone please give me a hint on how to inject services and which part of the given code goes in the link and/or the compile?
There is no specific issue with TypeScript regarding dependency injection. Simply define the dependencies you want to inject as parameters of checklistModel. If you want to ensure that the dependencies can be resolved after a minification of the javascript files, you can define the dependencies additionally via the $inject property (This will work in normal js as well):
module myModule {
'use strict';
checklistModel.$inject = ['$parse', '$compile'];
export function checklistModel($parse, $compile): ng.IDirective {
return {
link: function() { ... }
};
};
angular.module('myModule').directive('checklistModel', checklistModel);
};
Everything else is normal angular stuff. (Beside that, the type IDirective will tell you how the return-value should look like)
I hope this will help.

Ember: Access ember data 'store' object from utility class

I have a utility class for validating usernames in my ember application and have it setup as specified in the ember-cli docs. I do client-side username validation in several places in my application (components and controllers) so I wanted to pull the validation logic out into a reusable method.
The file is at /app/utils/username-validator.js and I can successfully include the file in my app by importing it like so: import usernameValidator from 'my-app/utils/username-validator';
This works great so far and I've used the pattern for several utility classes. The problem I'm running into now is that I'd like the username-validator method to include a check to see if the username already exists.
As I am using Ember-Data I'd like to check the Ember-Data store. By default, the store appears to only be accessible in controllers and routes. How can I make it accessible in my utility class? All the injection examples I've seen deal with injecting the store into other first class Ember objects like components.
Is it possible to inject the store into a simple utility class as well?
Thank you!
I am using the following versions:
Ember-cli v0.2.6
ember.debug.js:4888 DEBUG: -------------------------------
ember.debug.js:4888 DEBUG: Ember : 1.12.0
ember.debug.js:4888 DEBUG: Ember Data : 1.0.0-beta.18
ember.debug.js:4888 DEBUG: jQuery : 1.11.3
ember.debug.js:4888 DEBUG: Ember Simple Auth : 0.8.0-beta.2
ember.debug.js:4888 DEBUG: -------------------------------
===== Updated with detailed solution based on answer from torazaburo ======
Creating a service works great. Here is how I did it using ember-cli (v0.2.6) and ember v1.12.0
Create your service inside of /app/services/<service-name>.js
The service blueprint will look like this (note the name of the service is based on the name of the file):
import Ember from "ember";
export default Ember.Service.extend({
myFunction: function(){
}
});
Create an initializer for your service in /app/initializers/<service-name>.js which is used to inject your service into the different top level Ember objects (such as routes, controllers, components etc). Note that the file name of the initializer should match the file name of your service.
The blueprint for the initializer will look like this:
export function initialize (container, app) {
// Your code here
}
export default {
name: '<service-name>',
initialize: initialize
};
To give a concrete example, lets say your service is called validator and contains a bunch of validation routines. You want to inject the validator into all controllers, and you also want to inject the Ember Data store into the validator itself. You can do it like this:
export function initialize (container, app) {
// Inject the Ember Data Store into our validator service
app.inject('service:validator', 'store', 'store:main');
// Inject the validator into all controllers and routes
app.inject('controller', 'validator', 'service:validator');
app.inject('route', 'validator', 'service:validator');
}
export default {
name: 'validator',
initialize: initialize
};
Make your utility into a "service", into which you can inject the store. Actually, it sounds like your utility should be a service anyway, even if it doesn't need the store. By making it a service, for instance, it becomes much easier to stub it out when writing tests. With a service, you need neither import anything nor do any global injections in initializers, you can simply say
export default Ember.Component.extend({
myService: Ember.inject.service(), // inject services/my-service.js
foo: function() {
this.get('myService').api1(...);
}
});

Categories

Resources