Having an assets folder with :
/assets/images/image1.png
/assets/svg/svg1.svg
How to include it in an HTML template (not in a CSS file) in order to have angular/webpack to automatically enable cache-busting on it (transforming automaticallt assets/images/image1.jpg to assets/images/image1-4d5678xc0v987654v.jpg?
The goal is to handle cache and refresh it soon ASAP when an existing file gets updated.
With webpack, I used to do a :
<img src="<%=require("./assets/img/image1.jpg")%>" />
The only solution I found with angular is requiring all my images in the .ts file but it's quite a pain to do :
const image1src = require(`../assets/images/image1.jpg`);
class Component {
image1 = image1src; // contains image1-4d5678xc0v987654v.jpg
}
// and in template : <img [src]="image2" />
Is there something simplier ?
ps: I don't want to handle a query paremeter or custom name myself
pps: I don't want to inject these files through CSS (and I know it works when files are getting injected by css)
ppps: using a PWA is not an option in my case
Thanks
I created a pipe for this so I don't need to create variables inside a component as you did.
import {Pipe, PipeTransform} from '#angular/core';
#Pipe({
name: 'imgURL'
})
export class ImgURLPipe implements PipeTransform {
transform(value: string): string {
return require('../../../images/' + value); // specify here a correct src to your folder with images
}
}
To make the require works inside components or pipes, add this to your typings:
declare var require: {
<T>(path: string): T;
(paths: string[], callback: (...modules: any[]) => void): void;
ensure: (
paths: string[],
callback: (require: <T>(path: string) => T) => void
) => void;
};
Inside a template, it looks like this:
<img [src]="'myImage.png' | imgURL">
Remember to add a declaration of the pipe to your module.
are you using angular-cli?
Check this article, it explains how to manage the assets with angular-cli: https://kimsereyblog.blogspot.com/2017/09/manage-assets-and-static-files-with.html
Other solution would be to use css, eg:
background: url('../assets/fonts/roboto-v15-latin-regular.svg')
Related
In AngularJS i used$ rootScope to pass user data, for example:
$ rootScope.user = {
id: '4',
username: 'user'
...
};
$ rootScope.user.authenticated = false;
the data in $ rootScope was filled in every time a page was opened or updated using a query toSQL.
In Angular 9 i did not find the use of$ rootScope.
Tell me, where can such data be stored in Angular 9 and with what help can this functionality be implemented?
In angular, if you need anything like that, you create a service, provide it in root and inject it wherever you want it. For example:
The service:
// Create the service (providedIn: 'root') makes it available globally
#Injectable({providedIn: 'root'})
export class UserService {
user: any ={
id: '4',
username: 'user'
...
};
}
Using the service in a component:
#Component({...})
export class MyComponent implements OnInit {
_isAuthenticated: boolean;
// Inject the service
constructor(private _userService: UserService) {}
ngOnInit() {
// Using the service
this._isAuthenticated = _useService.user?.authenticated ?? false;
}
}
PS: The code above uses two interesting typescript features (which are new as I write this answer): optional chaining and Nullish Coalescing. You can always use a regular ternary operator instead of that:
this._isAuthenticated = _useService.user ? _useService.user?.authenticated : false
When my team and I migrated one of the projects from AngularJS to Angular, I took a look at when $rootScope was being used in the old app and it turns out it was used for identity/authentication 95% of the time. A few other use cases were regarding the spinner, browser related settings and edge cases.
It seems like your use case is similar to ours as well. I just folded that $rootScope.user into an existing AngularJS service called identity (or it could be auth, or whatever). So in each component that referred to that $rootScope.user, I replaced it with the following. The constructor is just dependency injection, allows you to use the variables within the identity service anywhere.
whatever.component.ts
currentUser = this.identity.currentUser
constructor(private identity: IdentityService) {}
The identity service looks something like the below. There's a getter function for the current user, and when it's not available, you look into the cookie, or otherwise, it's blank object (unauthenticated).
identity.service.ts
private _currentUser; //should only obtain currentUser via get currentuser()
constructor(private cookieService: CookieService) {}
get currentUser() {
if (!this._currentUser) {
this._currentUser = this.getUserFromCookie() || {}; //get from cookie
}
return this._currentUser;
}
getUserFromCookie() {
return this.cookieService.get('currentUser') ? JSON.parse(this.cookieService.get('currentUser')) : {};
}
Hopefully this gets you started and helps others as well.
I was wondering if there is any possibility to add some functions to the prototype of a class in Typescript.
My situation is as this:
I've got a file with a class, e.g. image.ts:
export class Image {
b64img: string;
}
This class is generated and so I can't add methods to it. I'm using this class in other generated classes, so creating a new class with extends Image is not possible for adding functions.
Is there any option that I can add a function to my class in a separated file so I could use it on all objects of the class Image?
Thanks
You can. You can extend it by defining an extension to the module it is defined in.
Create a file called image-extension.ts or something like that.
Now, if in that file you would import Image like this:
import { Image } from `image`;
declare module 'image' { // same name than in the import!
export interface Image {
newMethod: () => void;
}
}
Image.prototype.newMethod = function() {
console.log('new method');
}
Now, wherever you import this file, you can use that method:
import { Image } from 'image';
import 'image-extension'; // import just for side-effects
Of course, you can also add the declare module to a d.ts file, so it gets loaded automatically, and in another file add the true functions to the prototype, making sure that such a file gets loaded in your application
There is a set of components in my Angular 4 app in which each component
requires a certain set of properties from a different JSON file which it shows on its template.
I have created a common JSON file containing all the properties and I load it when the app-component is called using a service that holds those properties array.
I then inject that same service into different components and fetch the populated array. The values show in the HTML all fine.
However, this approach seems to be a bit time consuming especially when the constants grow in size. Loading thousands of constants all at once and injecting them into different components where few of them are required is not a good approach.
I was willing to work on an approach where I create specific contansts JSON file for each component and somehow load it when the component is actually initialized. This way I can save the burden of a heavy JSON object and only those properties would be loaded that are required by that component.
The load() method in my constants service looks something like this:
#Injectable()
export class ConstantsService {
constructor(private http: HttpClient) {
console.log('ConstantsService created');
}
constants = {};
load() {
var constants = {};
var cons = 'constants';
var constantsResourceUrl =
'path' + cons + '.json';
this.http.get(constantsResourceUrl)
.subscribe(result => {
this.constants = result;
},
error => this.log.error(constantsResource + ' could not be loaded')
);
}
}
And my Components look like this to get the value of the constants:
#Component({
selector: 'xyz',
templateUrl: './xyz.html',
styleUrl: './xyz.css'
})
export class MyComponent {
consts = {};
constructor(private constantsService: ConstantsService) {
consts = this.constantsService.constants;
}
}
Any help would be appreciated.
If did have similar dilema. To use db or json files for settings/parametars behavior. Dynamic content end up in db, and at the end I found that is little stupid to fetch and use http request to get static json content since that could be bundled in source. I did split it and use in multiple exported constant in ts. file like:
export const dummyLookupConst = `{
"queryNo": 0,
"id": 250,
...}
and then import it into components by need.
Styles like
<div [style.background-image]="\'url(\' + image + \')\'">Background</div>
<div [style.transform]="rotate(7deg)"
are not added anymore
update (2.0.0 final)
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({name: 'safeHtml'})
export class SafeHtml implements PipeTransform {
constructor(private sanitizer:DomSanitizer){}
transform(html) {
return this.sanitizer.bypassSecurityTrustStyle(html);
// return this.sanitizer.bypassSecurityTrustHtml(html);
// return this.sanitizer.bypassSecurityTrustScript(html);
// return this.sanitizer.bypassSecurityTrustUrl(html);
// return this.sanitizer.bypassSecurityTrustResourceUrl(html);
}
}
See also https://angular.io/api/platform-browser/DomSanitizer
<div [innerHTML]="someHtml | safeHtml"
update
DomSanitizationService is going to be renamed to DomSanitizer in RC.6
original
This should be fixed in RC.2
See also Angular2 Developer Guide - Security
Angular2 intruduced sanitization of CSS values and property binding like [innerHTML]=... and [src]="..." in RC.1
See also https://github.com/angular/angular/issues/8491#issuecomment-217467582
The values can be marked as trusted by using DomSanitizer.bypassSecurityTrustStyle(...)
import {DomSanitizer} from '#angular/platform-browser';
...
constructor(sanitizer: DomSanitizationService) {
this.backgroundImageStyle = sanitizer.bypassSecurityTrustStyle('url(' + this.image + ')');
// for HTML
// this.backgroundImageStyle = sanitizer.bypassSecurityTrustHtml(...);
}
and binding to this value instead the untrusted plain string.
This can also be wrapped in a pipe like
#Pipe({name: 'safeStyle'})
export class Safe {
constructor(private sanitizer:Sanitizer){}
transform(style) {
return this.sanitizer.bypassSecurityTrustStyle(style);
// return this.sanitizer.bypassSecurityTrustHtml(style);
// return this.sanitizer.bypassSecurityTrustScript(value);
// return this.sanitizer.bypassSecurityTrustUrl(value);
// return this.sanitizer.bypassSecurityTrustResourceUrl(value);
}
}
<div [ngStyle]="someStyle | safeStyle"></div>
with
someHtml = `click to see the awesome`;
is still working though :-[ (it's work in progress)
Plunker example (Angular 2.0.0-rc-1)
See also Angular 2 Security Tracking Issue
and https://angular.io/docs/ts/latest/api/platform-browser/index/DomSanitizer-class.html
Hint about {{...}}
Sanitized content can't be bound using prop="{{sanitizedContent}}" because {{}} stringyfies the value before it is assigned which breaks sanitization.
Bypassing sanitizer to trust any content can be a security concern. Since Angular is not a dedicated sanitizing library, it is overzealous towards suspicious content to not take any risks. It removes almost all attributes, for example. You can delegate sanitizing to a dedicated library — DOMPurify. Here's a wrapper library I've made to easily use DOMPurify with Angular.
https://github.com/TinkoffCreditSystems/ng-dompurify
It also has a pipe to declaratively sanitize HTML:
<div [innerHtml]="value | dompurify"></div>
One thing to keep in mind is DOMPurify is great for sanitizing HTML/SVG, but not CSS. So you can provider Angular's CSS sanitizer to handle CSS:
import {NgModule, ɵ_sanitizeStyle} from '#angular/core';
import {SANITIZE_STYLE} from '#tinkoff/ng-dompurify';
#NgModule({
// ...
providers: [
{
provide: SANITIZE_STYLE,
useValue: ɵ_sanitizeStyle,
},
],
// ...
})
export class AppModule {}
It's internal — hense ɵ prefix, but this is how Angular team use it across their own packages as well anyway.
So here is the problem, I am attempting to create a new component from within a service that is injected within the App Component. I need the new component to be placed within the app component html tag not outside. The thing is I really do not want the app component to have to provide anything to the service I may need to inject the service into other places and hence not have it tightly coupled to the app component. So far I have created a DIV at the end of the app component html and then used #ViewChild to read the ViewContainerRef from this element located within the app component. This is then provided to the service via a function call so that it can make use of createComponent. This allows for the NEW component to be placed within the scope of the app component, not within the body. Unfortunately this is too dependant on the app component providing the ViewContainerRef. Any ideas of how I can create the new component as described.
Code Example
app.component.html
<app-component>
<div #newCompHook></div>
</app-component>
app.component.ts
#ViewChild('newCompHook', {read: ViewContainerRef}) newCompViewRef: ViewContainerRef;
constructor(appService: AppService) {
appService.setViewRef(this.newCompViewRef);
}
app.service.ts
private myViewRef;
constructor(private compiler: ComponentResolver){
this.myViewRef = null;
}
public setViewRef(vr: ViewContainerRef): void {
this.myViewRef = vr; // <-- DO NOT WANT TO DO THIS !!!
}
public createNewComp(childCmp: Type): void {
if (this.myViewRef !== null){
this.compiler.resolveComponent( childCmp ).then((compFactory:ComponentFactory) => this.myViewRef.createComponent(compFactory) )
}
}
createNewComp is called by an external source and may or may not provide the childCmp type to be resolved.
So any ideas of how I can do this without needing to provide anything from the app component ???
If you need to have the viewContainerRef in your service that is the only solution...
But it is not a good practice to generate HCI components in a service. It's the role of other components.
Let's take an exemple : your server send you a list of objects (a list of strings for exemple) and you want to generate a button for each string.
In your service you just manage the string list :
#Injectable()
export class MyService {
private myModel : Array<String> = new Array();
public get MyModel () : Array<String> {
return this.myModel;
}
/// Assume you have method in the service to populate the model...
}
Then it's your component which generate the HCI :
export class AppComponent {
/// Use dependency injection to get service :
constructor (private _myService : MyService){}
public get MyModel () : Array<String> {
return this.myService.MyModel;
}
}
Finally in your component template :
<div>
<ul>
<li *ngFor="let s of MyModel">
<!-- Button with your model text -->
<button>s</button>
</li>
</ul>
</div>
That is a better solution than generate the components in the service because just imagine you don't want buttons list but a togglebuttons list in your HCI, here you just have to change the HTML. The service is still the same, and the components typescipt part is still the same too !