Getting JSON values/properties from a file in a component in angular 4 - json

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.

Related

There is no `$ rootScope` in` Angular9`. What can be used?

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.

Inject image/svg/whatever in an angular template with cache hash handling

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')

Pass JSON data from App Component to another component in Angular 6

I have two components,
1. App Component
2. Main Component
app.component.ts
ngOnInit () {
this.httpService.get('./assets/batch_json_data.json').subscribe(data => {
this.batchJson = data as string [];
}
I am able to get the JSON from a file into 'batchJson' and need to pass this to my main component for further operations.
There is no event or anything that triggers this.
I have not implemented anything yet, I am trying to read #Input, #Output etc but do not understand how it works and need to go through it some more.
I have just declared basics in the main.component.ts
import { Component, OnInit, ViewChild, Input } from '#angular/core';
import { AppComponent } from '../app.component';
export class MainComponent implements OnInit {
}
Please help me out, I am an absolute rookie in Angular and am unable to try anything because my concepts are not clear and I did browse Stack Overflow, the answers are not matching my requirements.
One solution could be to use a public BehaviorSubject in your app.component.ts.
public batchJson$ = new BehaviorSubject<JSON>(null);
ngOnInit () {
this.httpService.get('./assets/batch_json_data.json').subscribe(data => {
this.batchJson = data as string [];
this.batchJson$.next(JSON.parse(data));
}
Then in your main.component.ts
constructor(private _appComponent : AppComponent )
ngOnInit(){
this._appComponent.batchJson$.subscribe((data)=>{
if(data != null){
//set data to local variable here
}
})
}
Typically I store this kind of logic in a Service, using this in a component will definitely get you pointed in the right direction to learning this concept. Preferably your component should be responsible for interacting with the UI and rendering data, while your services handle retrieving and distributing data.
you can implement common service which does all related http operations and you can inject this service in any component u want and read the json.
Make sure you return the http.get and you subscribe to it where ever you call this method.
If you are not aware of services , you can read about creating and injecting services in angular
You can use rxjs subject to emit the data through out the app and fetch it anywhere by using subject.getValue() method.
First of all you should spare time on understanding the concept of any technology before you start working on it. Else you would be spending most of the time seeking help.
I had created demo here - https://stackblitz.com/edit/angular-lko7pa. I hope it will help you out.

How to parse Date object properly of TypeScript class when HttpClient mapping won't?

Task.ts:
export class Task {
name: string;
dueDate: Date;
}
tasks.service.ts:
#Injectable()
export class TasksService {
constructor(private http: HttpClient) { }
getTasks(): Observable<Task[]> {
return this.http.get<Task[]>(`${WEBAPI_URL}/Tasks`);
}
}
The Task objects I get back from getTasks() have their dueDate field assigned but the value is of type string instead of Date like I would expect.
Some searching lead me to this issue on the Angular github which made clear to me that HttpClient has no intent of properly parsing my object. Unfortunately the issue didn't give helpful guidance about what I should actually be doing to get my Date object. What do I do?
You have several options here.
1) You can deal with the date as a string in the UI. So change the definition of Task to retain the date as a string and work with it that way, converting it to a date for calculations as needed.
2) You can map each object coming back from the Http request to a Task object. Something like this:
getTasks(): Observable<Task[]> {
return this.http.get<Task[]>(`${WEBAPI_URL}/Tasks`)
.pipe(
map(items => {
const tasks: Task[] = [];
return items.map(
item => {
item.dueDate = new Date(item.dueDate);
return Object.assign(new Task(), item);
});
}),
tap(data => console.log(JSON.stringify(data))),
catchError(this.handleError)
);
}
This also have the benefit of having actual task objects in your array, meaning that if you ever add Task methods or getters/setters they will be correctly associated with your tasks array.
EDIT:
It may be better to build a utility class that handled the serialization/deserialization of your objects. Then the above code would look more like this:
getTasks(): Observable<Task[]> {
return this.http.get<Task[]>(this.url)
.pipe(
map(TaskSerializer.serialize),
catchError(this.handleError)
);
}
declare it as a date in the component like this:
example.component.ts
constructor(private taskService: TaskService) {
}
ngOnInit() {
this.taskService.getTaksks().subscribe(response => {
tempValue = response.body;
tempValue.dueDate = new Date(tempValue.dueDate.format('MM-DD-YYYY');
});
}
OR save it as an instant
Task.ts
export class Task {
name: string;
dueDate: Instant;
}
I would suggest doing the first way. I would also suggest looking at the moment.js library
EDIT: I would declare it as a Date object and let it store it as a string on the db. That is how I have seen most use cases for dealing with dates and it is how everyone in my company has dealt with dates. 99% of the time you just want the month/day/year so it makes sense that you will store only that on the db, but it is a little cumbersome to format it to a date object on the ui side.
I found a not so heavy interceptor, allowing to have directly correct Date in objects when using HTTP calls in Angular. You can find it here: https://dev.to/imben1109/date-handling-in-angular-application-part-2-angular-http-client-and-ngx-datepicker-3fna
Please note that the only thing I had to change was the regex, in order to make the Z at the end optional and I use the DateTimeFormatter.ISO_DATE_TIME format on server side.

Vuex how to declare constant root mutations

So I have a very similar Application Structure to the example below
https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart
However, there is no root mutations defined there. I need to create one since I have to update the state that is dynamically created in vue-tables-2.
In my mutation-types.js I declared the name of the mutation as
export const UPDATE_CLIENTTABLE = 'UPDATE_CLIENTTABLE'
However, I'm not sure how to write the mutations in mutations.js There's no module defined because the state is dynamically created by vue-tables-2. I'm doing something like below but it doesn't work
[types.UPDATE_CLIENTTABLE] (state, data) {
state.ClientTableLine.data = data
}
You'll need to export your mutation in your mutations.js file
export const [types.UPDATE_CLIENTTABLE) = (state, data) => {
state.ClientTableLine.data = data
}
Then import it into your vuex set up
import * as mutations from './mutations'
export default new Vuex.Store({
mutations,
actions,
getters,
modules: {
cart,
products
},
strict: debug,
plugins: debug ? [createLogger()] : []
})
This is assuming your vuex set up is taken from the shopping car example