Asked  2 Years ago    Answers:  5   Viewed   4.5k times

I have a dotnetcore 20 and angular4 project that I am trying to create a userService and get the user to my home component. The backend works just fine but the service doesn't. The problem is on localStorage. The error message that I have is :

Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.

And my userService

import { User } from './../models/users';
import { AppConfig } from './../../app.config';
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions, Response } from '@angular/http';



@Injectable()
export class UserService {
constructor(private http: Http, private config: AppConfig) { }

getAll() {
    return this.http.get(this.config.apiUrl + '/users', this.jwt()).map((response: Response) => response.json());
}

getById(_id: string) {
    return this.http.get(this.config.apiUrl + '/users/' + _id, this.jwt()).map((response: Response) => response.json());
}

create(user: User) {
    return this.http.post(this.config.apiUrl + '/users/register', user, this.jwt());
}

update(user: User) {
    return this.http.put(this.config.apiUrl + '/users/' + user.id, user, this.jwt());
}

delete(_id: string) {
    return this.http.delete(this.config.apiUrl + '/users/' + _id, this.jwt());
}

// private helper methods

private jwt() {
    // create authorization header with jwt token
    let currentUser = JSON.parse(localStorage.getItem('currentUser'));
    if (currentUser && currentUser.token) {
        let headers = new Headers({ 'Authorization': 'Bearer ' + currentUser.token });
        return new RequestOptions({ headers: headers });
    }
}

And my home.component.ts is

import { UserService } from './../services/user.service';
import { User } from './../models/users';
import { Component, OnInit } from '@angular/core';

@Component({
moduleId: module.id,
templateUrl: 'home.component.html'
})

export class HomeComponent implements OnInit {
currentUser: User;
users: User[] = [];

constructor(private userService: UserService) {
   this.currentUser = JSON.parse(localStorage.getItem('currentUser'));
}

ngOnInit() {
   this.loadAllUsers();
}

deleteUser(_id: string) {
   this.userService.delete(_id).subscribe(() => { this.loadAllUsers() });
}

private loadAllUsers() {
   this.userService.getAll().subscribe(users => { this.users = users; });
}

The error is on JSON.parse(localStorage.getItem('currentUser'));

 Answers

5

As the error says, localStorage.getItem() can return either a string or null. JSON.parse() requires a string, so you should test the result of localStorage.getItem() before you try to use it.

For example:

this.currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');

or perhaps:

const userJson = localStorage.getItem('currentUser');
this.currentUser = userJson !== null ? JSON.parse(userJson) : new User();

See also the answer from Willem De Nys. If you are confident that the localStorage.getItem() call can never return null you can use the non-null assertion operator to tell typescript that you know what you are doing:

this.currentUser = JSON.parse(localStorage.getItem('currentUser')!);
Monday, September 5, 2022
 
niru
 
4

There is a very simple approach and a very complex one.

The simple approach is to use raw HTML with anchor element outside of angular without RouterLink. Register to clicks on that anchor element and use the Router service to navigate.

The task was to fire links but the actual problem is far deeper, now it links next time its showing an angular component...

So, for the complex solution:

This is an highly advanced topic... Not only it involves using advanced angular techniques it's also advanced in the leaflet implementation.

I'll do my best to convey the message but due to the complexity the examples will be very simple and will require work.

First - Angular realm.

An HTML string that contains directives, components or pipes will never work, the only way is to initialize a View

Let's define A View as a reference to view instance of a component or a template.

These are called ComponentRef and TemplateRef

So, we have 2 ways to solve this problem. Since I can't do both i'll go with ComponentRef but note that you can also use TemplateRef. With templates you'll first need to obtain a template defined in the component as well as a ViewContainerRef to attach that template to.

We will build a service that accepts a leaflet Marker and binds to the click event of the marker, on click it will open a popup which is an angular Component.

The component is simple, it renders a link.

@Component({
  selector: 'facility-link',
  template: `Facility <br/> <a routerLink="{{link}}"> View Two</a>`
})
export class FacilityLinkComponent {
  public link: string;
  constructor() { }
}

Now, for the service:

@Injectable()
export class LinkPopupService {

  constructor(private cfr: ComponentFactoryResolver,
              private injector: Injector,
              private appRef: ApplicationRef) { }


  register(marker: leaflet.Marker, link: string): void  {
    marker.on('click', ($event: leaflet.MouseEvent)  => this.popup($event.target, link) );
  }

  popup(marker: leaflet.Marker, link: string) {
    const cmpFactory = this.cfr.resolveComponentFactory(FacilityLinkComponent);
    const componentRef = cmpFactory.create(this.injector);
    componentRef.instance.link = link;
    this.appRef.attachView(componentRef.hostView);
    const markerElement = marker.getElement();
    markerElement.parentElement.appendChild(componentRef.location.nativeElement);

    const markerPos = leaflet.DomUtil.getPosition(markerElement);
    const markerClass = leaflet.DomUtil.getClass(markerElement);


    leaflet.DomUtil.setTransform(componentRef.location.nativeElement, markerPos);
    leaflet.DomUtil.setClass(componentRef.location.nativeElement, markerClass);
  }
}

The register method accepts a marker and the link and registers to the click event.

When the popup method fires it uses angular tools to create a view instance of FacilityLinkComponent, set the link for future binding, attach a view to it and attach it to the DOM.

This all happens in the first 5 lines of code.

Some notes:

  • We must attach a view so change detection works
  • A Proper implementation will allow to set ViewContainerRef and / or an Injector - this is a must when using lazy loading.
  • It is preferred sending data to the component via Injector and not by assignment (ReflectiveInjector)
  • Proper clean up is required (destroy the component and detach the view)
  • Need to add toggle logic, also clean on navigation.

Leaflet

The code from the 6th line performs positioning of the popup.

This is a very simple logic, it just copies everything from the marker.

This is why I used a marker, so I'll have a reference to take the positioning from.

In a realworld example you'll need to get a panel and push the components into their own layer, computing the position. This is not that difficult since leaflet has all the helper, but it was too much for this.

Hope it helps.

Sunday, August 14, 2022
 
kapilgm
 
1

Since getElementById possibly returns null. So you just simply check before using like:

function Portal1({ children }) {
  return portalDiv ? ReactDOM.createPortal(<>{children}</>, portalDiv) : null;
}
Sunday, October 2, 2022
 
2

Try to import MdRippleModule in your AppModule:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { HttpModule} from '@angular/http';
import { MdRippleModule } from '@angular2-material/core/core'; <== this line

@NgModule({
  imports:      [ BrowserModule, HttpModule, MdRippleModule ], <== add here
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Or pass MdRipple directive within your component:

...
import { MdRipple } from '@angular2-material/core/core'; <== this line
@Component({
  moduleId: module.id,
  selector: 'nav-bar',
  templateUrl: 'nav-bar.component.html',
  styleUrls: ['nav-bar.component.css'],
  directives: [ 
    MdToolbar, 
    MdButton,
    MdIcon,
    MdRipple <== add here
  ],
  providers: [ 
    MdIconRegistry 
  ]
})
export class NavBarComponent implements OnInit {
...
Sunday, December 18, 2022
 
nomada
 
5

Whilst result has the potential to return a string, it cannot implicitly cast this to a string as there is a risk of data loss. i.e. ArrayBuffer as string may result in data truncation (would have to test). So you have to explicitly cast it as to tell the compiler "I know what I am doing".

2 approaches to achieve this are:

(string)fileReader.result;
fileReader.result as string;


Folks, Check @Malvolio's answer, it's more complete.
Monday, November 14, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share

Browse Other Code Languages