I'm reading lots of articles on this matter like this, and this and also this but each one of these articles starts from a situation in which the NG1 service is a class and can be exported.
I'm in a very different situation, i often have multiple services in the same file and they are defined in a very old style manner like
angular.module('App.services').factory('SessionService',
function() {
var defer = $q.defer();
[...]
}
);
No class, no export.
And this stuff is directly linked in the page with an old fashioned <script src="...">
At the same time i'm trying to create new directives in Angular2 and these directives need those old fashioned services.
I get i should be able to write something like this
import {Injector,Component, Directive, ElementRef, Input, View} from 'angular2/core';
var injector = Injector.resolveAndCreate([
SessionService
]);
var SessionService = injector.get(SessionService);
#Component({
selector: 'nav-bar'
})
#View({
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor() {
}
}
but of course SessionService object is not found.
How can i get out of this mess?
[Additional Info]
Using babel as transpiler
angular2-annotations plugin added
A great article to understand the difference between Annotations and Decorators in angular2: http://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html
You simply need to leverage #Inject:
#Component({
selector: 'nav-bar'
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor(private #Inject('SessionService') sessionService) {
}
}
See this plunkr for more details: http://plnkr.co/edit/U6ygjUUQ04mTGAAaC1pZ?p=preview
You can notice that with factory you can't use classes. It's only possible with services...
If you use ES6 only, you could try this:
#Component({
selector: 'nav-bar'
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor(sessionService) {
}
static get parameters() {
return [[ new Inject('...'), new Inject('...') ]];
}
}
Related
I have this application where I do rest call to a server and receive a JSON response which contains the name of an particular UI setting.
Let's assume the data received from server is as follows,
Response :-
{
"uiSetting" : "ui-type-one"
}
There can be multiple UI settings like ui-type-two, ui-type-three and so on.
Is there any way in angular to decide which component to load at runtime based on this uiSetting value Like following
#Component({
selector: 'base-strategy',
templateUrl: './base.component.html',
styleUrls: ['./base.component.css']
})
export class BaseStrategy {
}
#Component({
selector: 'ui-type-one-strategy',
templateUrl: './UiTypeOne.component.html',
styleUrls: ['./ui-type-one.component.css']
})
export class UiTypeOneStrategy extends BaseStrategy {
}
#Component({
selector: 'ui-type-two-strategy',
templateUrl: './UiTypeTwo.component.html',
styleUrls: ['./ui-type-two.component.css']
})
export class UiTypeTwoStrategy extends BaseStrategy {
}
HTML will be like this.
<!-- If response received is ui-type-one this should show UI of ui-type-one-strategy.
If response received is ui-type-two this should show UI of ui-type-two-strategy. and so on.
-->
<base-strategy></base-strategy>
I am aware of ng-template in angular but that I want to use as last option, I am new to angular hence wanted to know if there is any such way to define UI strategy based on response.
You can create components dynamically using Dynamic Component Loader.
Angular provides the ComponentFactoryResolver which can be used along with ViewContainerRef to dynamically load components.
In your case, maybe based on the response, you can dynamically load the component.
Glimpse of the code:
async load(comp: string) {
let component: Type<any>;
switch (comp) {
case 'A': {
component = (await import('./comp-a/comp-a.module')).default;
break;
}
case 'B': {
component = (await import('./comp-b/comp-b.module')).default;
break;
}
}
const factory = this.cfr.resolveComponentFactory(component);
this.container?.clear();
this.container.createComponent(factory);
}
Demo: https://stackblitz.com/edit/angular-ivy-n2zrqt?file=src%2Fapp%2Fapp.component.ts
Docs: https://angular.io/guide/dynamic-component-loader#loading-components
In Angular v13, components can be created without the need for ComponentFactoryResolver. Check out the blog here: https://blog.angular.io/angular-v13-is-now-available-cce66f7bc296
In a simple angular app, I have created a test component like the one below:
test.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'test',
template: `<h1>I am a test</h1>`,
styles: [`h1 { font-family: Lato; }`]
})
export class TestComponent {
}
which I import in my app module like this:
app.component.ts
import { Component } from '#angular/core';
import { TestComponent } from './test.component';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
public TestComponent = TestComponent;
dynamic = "Test";
constructor(){
console.log(TestComponent);
console.log(this.TestComponent);
console.log(this['TestComponent']);
console.log(this[this.dynamic + 'Component']);
}
}
Getting help from this topic Angular 5 Dynamic variable name, I was able to get my component name dynamically (in the real example, the dynamic variable changes depending on an API call response).
My question is if there is any way to achieve the same result without having to pass the component as a local class variable, thus avoiding this line
public TestComponent = TestComponent;
without the this keyword the dynamic variable is not working, it gives me an error
console.log([this.dynamic + 'Component']);
I tried adding the code in a stackblitz but cannot seem to be able to share it (https://github.com/stackblitz/core/issues/215), but it is quite simple to copy the two files in a new empty stackbitz (https://stackblitz.com/edit/angular-f4ars5) to replicate.
Thanks in advance.
First you need to understand the following line
this['TestComponent']
In javascript you can access class properties in two ways. Class.property or Class["property"]. So when you are writing this["TestProperty"] you are effectively saying, Get AppComponentInstance.TestProperty
So console.log([this.dynamic + 'Component']); is like writing .TextComponent with nothing on the left side.
Now based on this, what are you trying to achieve exactly? Get the type or an instance of the type? What do you want to use the type for?
What you could do though would be to declare somewhere another global class and have these properties in them. Check this one for global variable declaration.
Your json could be something like this:
export class ComponentMapping
{
private TestComponent = TestComponent;
}
And then in your class access the test component accordingly.
I am trying to use the Typed.js library in Angular CLI, but I couldn't manage to make it work. I followed the instructions in How can I use JavaScript code in Angular 7?: copied the jQuery library and typed scrips in assets folder, added a path to the files in angular.json, but I am not sure what to do in the component.ts file:
This is my component.ts code:
import { Component, OnInit } from '#angular/core';
declare var jQuery: any;
declare var Typed: any;
#Component({
selector: 'app-typing',
templateUrl: './typing.component.html',
styleUrls: ['./typing.component.scss']
})
export class TypingComponent implements OnInit {
constructor() { }
ngOnInit() {
var typed = new Typed('.typed', {
strings: ["First sentence.", "Second sentence."],
typeSpeed: 30
});
}
onComplete() {
}
}
I am pretty sure what I wrote in ngOnInit() is wrong, but how can I make it right?
You have to import typed.js in order to be able to use it. Declaring it as variable with type of any does not make any sense. Try importing it instead like this:
import Typed from 'typed.js';
I have a module in Angular that is structured likes this:
moduleName
componentA
componentB
Now componentA and componentB are very similar, as they share some attributes and methods, e.g.:
protected available: boolean = true;
As I don't want to repeat myself, I've created a base class, that stores all this:
export abstract class BaseComponent {
protected available: boolean = true;
}
And both controllers inherit from that class:
import { BaseComponent } from '../base.component';
export class ComponentA extends BaseComponent implements OnInit {
constructor() {
super();
}
ngOnInit() {
console.log(this.available);
}
}
This works just fine. However, when I research this soultion a lot of people are saying:
Don't use inheritance, use composition in this case.
Alright, but how can I use composition instead? And is the gain really that big over the current solution?
Thanks a lot for your time.
For composing objects in angular you need to have a reference to that object inside of your class, which shares data and functionality. To do that you need to use Angular services, and inject them to your class, and there should be 1 instance of service per component.
Create a new service by running ng g s my-service, remove providedIn: 'root' from your service annotation (We want to provide instance per component)
Add public available: boolean = true; to the service
provide the service through the components, in #Component configs on your components
inject the service in your both component constructors, constructor(private myService:MyService)
Now you have a composition that keeps data and functionality
#Component({
selector: 'app',
templateUrl: './app.my-component.html',
styleUrls: ['./app.my-component.css'],
providers: [MyService]
})
export class MyComponent {
constructor(private myService: MyService) {
}
}
If you create same components with big part same logic. you can use inheritance for example controlSelectComponent and controlInputComponent stackblitz example
For composition you need to create service and provide it to both components. But you dont keep component state in service becose all service are singletone. And when one component change state another component crash.
You also can provide service to each component in providers section
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [MyService]
})
export class AppComponent {
constructor(private myService: MyService) {
}
}
But in case with saving state in service is not the best solution
Conclusion
Use services and composition for share helper methods between components.
Use abstract class and inheritance for components with same logic and state changes.
I would also recommend to read about Composition over Inheritance. The syntax(InversifyJs) is very similar that Angular uses. Please see this blog
I have a function X within a JavaScript file “MyFile.js” in the following path of an Angular 4 project : /assets/js/MyFile.js
I already added the path in the angular-cli.json into the scripts section.
...
“scripts”:
[ “assets/js/MyFile.js”]
...
Question: How can access the function X in MyFile.js from a typescript component?
I don't believe that you can call a function from a script added via .angular-cli.json, although you can implement IIFEs that way. Normal usage is for external libraries (bootstrap.js, collapse, animations and the like) as they get added as <script> tags in the index.html.
Rather than adding it to your angular-cli.json, it's easier to import it in your components
Give MyFile a .ts extension to avoid having to pass the --allowJs flag. E.g., MyFile.ts
Make sure to export your functions. If you have several, you might put them in a class and make them static (but that's a different question). Here's what I did:
_
// myfile.ts
export const myFunction = () => {
console.log('hello');
};
_
Import the script into the desired component via import { myFunction } from '../assets/js/myFunction'
_
import { Component, OnInit } from '#angular/core';
import { myFunction} from '../assets/js/myfile';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
ngOnInit() {
myFunction();
}
}
_